Размер строки инициализации в Java - PullRequest
8 голосов
/ 30 ноября 2011

Видимо, есть ограничение на размер строки инициализации в javac.Может кто-нибудь помочь мне определить максимальный лимит, пожалуйста?

Спасибо

edit:

Мы создаем строку инициализации, которая будет выглядетьчто-то вроде этого "{1,2,3,4,5,6,7,8 ......}", но в идеале с 10 000 номеров.Когда мы делаем это для 1000, это работает, 10 000 выдает ошибку, говоря, что код слишком велик для оператора try.

Для создания этого мы используем построитель строк и зацикливаем массив, добавляя значения.По-видимому, это ограничение в Javac.Нам сказали, что мы можем перестроить массив в методе, который мы вызываем, если мы передадим его небольшими порциями.Это, однако, невозможно, потому что у нас нет контроля над пользовательским методом, который мы вызываем.

Я хотел бы опубликовать код, но не могу, потому что это проект для университета.Я не ищу решения для кода, только некоторую помощь в понимании того, какова реальная проблема здесь.

Это цикл for, который является нарушителем

    Object o = new Object() 
    { 
        public String toString() 
        { 
            StringBuilder s = new StringBuilder();
            int length = MainInterfaceProcessor.this.valuesFromData.length;
            Object[] arrayToProcess = MainInterfaceProcessor.this.valuesFromData;

            if(length == 0)
            {
                //throw exception to do
            }
            else if(length == 1)
            {
                s.append("{" + Integer.toString((Integer)arrayToProcess[0])+"}");
            }
            else
            {
                s.append("{" + Integer.toString((Integer)arrayToProcess[0])+","); //opening statement
                for(int i = 1; i < length; i++)
                {
                    if(i == (length - 1))
                    {
                        //last element in the array so dont add comma at the end
                        s.append(getArrayItemAsString(arrayToProcess, i)+"}");
                        break;
                    }       
                    //append each array value at position i, followed
                    //by a comma to seperate the values
                    s.append(getArrayItemAsString(arrayToProcess, i)+ ",");
                }
            }
            return s.toString();
        }
    };
    try 
    {
        Object result = method.invoke(obj, new Object[] { o });

}

Ответы [ 3 ]

19 голосов
/ 30 ноября 2011

Длина строкового литерала (то есть "...") ограничена структурой CONSTANT_Utf8_info формата файла класса, на которую ссылается структура CONSTANT_String_info.

CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

Ограничивающим фактором здесь является атрибут length, который имеет размер всего 2 байта, то есть имеет максимальное значение 65535. Это число соответствует количеству байтов в модифицированном представлении строки UTF-8 (на самом деле этопочти CESU-8 , но символ 0 также представлен в двухбайтовой форме).

Таким образом, чистый строковый литерал ASCII может иметь до 65535 символов, тогда как строка, состоящая изсимволов в диапазоне U + 0800 ... U + FFFF имеют только одну треть из них.А те, которые закодированы как суррогатные пары в UTF-8 (то есть U + 10000 до U + 10FFFF), занимают по 6 байтов каждый.

(То же ограничение существует для идентификаторов, то есть имен классов, методов и переменных,и введите для них дескрипторы, поскольку они используют одну и ту же структуру.)

В спецификации языка Java не упоминается никаких ограничений для строковых литералов :

Строкалитерал состоит из нуля или более символов, заключенных в двойные кавычки.

Таким образом, в принципе, компилятор может разбить более длинный строковый литерал на более чем одну CONSTANT_String_info структуру и реконструировать его во время выполнения путем конкатенации (и .intern() - результат).Я понятия не имею, если какой-либо компилятор фактически делает это.


Это показывает, что проблема не в строковых литералах, а в инициализаторах массива.

При передаче объекта в BMethod.invoke (и так же, как BConstructor.newInstance), это может быть либо BObject (то есть оболочка вокруг существующего объекта, затем он будет передавать обернутый объект), String (которая будет передана как есть) или что-нибудь еще.В последнем случае объект будет преобразован в строку (toString()), а затем эта строка будет интерпретирована как выражение Java.

. Для этого BlueJ обернет это выражение в класс / методи скомпилируйте этот метод.В этом методе инициализатор массива просто преобразуется в длинный список назначений массива ... и это, наконец, делает метод длиннее, чем максимальный размер байт-кода метода Java:

Значение элемента code_length должно быть меньше 65536.

Вот почему оно разбивается на более длинные массивы.


Итак, чтобы передать большие массивы, мы имеемчтобы найти какой-то другой способ передать их в BMethod.invoke.API расширения BlueJ не имеет возможности создавать массивы или получать к ним доступ, заключенные в BObject.

Одна идея, которую мы нашли в чате, такова:

  1. Создайте новый класс внутрипроект (или в новом проекте, если они могут взаимодействовать), что-то вроде этого:

    public class IntArrayBuilder {
        private ArrayList<Integer> list;
        public void addElement(int el) {
            list.add(el);
        }
        public int[] makeArray() {
            int[] array = new int[list.size()];
            for(int i = 0; i < array.length; i++) {
               array[i] = list.get(i);
            }
            return array;
        }
    }
    

    (Это для случая создания int[] - если вам нужны также другие типы массивов,его также можно сделать более универсальным. Кроме того, его можно сделать более эффективным, если использовать внутреннее int[] в качестве хранилища, время от времени увеличивая его по мере роста, а int makeArray делать окончательную копию массива. Это эскиз, поэтому этопростейшая реализация.)

  2. Из нашего расширения создайте объект этого класса и добавьте элементы к этому объекту, вызвав его метод .addElement.

    BObject arrayToBArray(int[] a) {
        BClass builderClass = package.getClass("IntArrayBuilder");
        BObject builder = builderClass.getConstructor(new Class<?>[0]).newInstance(new Object[0]);
        BMethod addMethod = builderClass.getMethod("addElement", new Class<?>[]{int.class});
        for(int e : a) {
            addMethod.invoke(builder, new Object[]{ e });
        }
        BMethod makeMethod = builderClass.getMethod("addElement", new Class<?>[0]);
        BObject bArray = (BObject)makeMethod.invoke(builder, new Object[0]);
        return bArray;
    }
    

    (Для эффективности объекты BClass / BMethod могут быть получены один раз и кэшированы, а не один раз для каждого преобразования массива.)
    Если вы генерируете содержимое массивов по какому-либо алгоритму, вы можете сделать это поколениездесь вместо того, чтобы сначала создавать еще один объект переноса.

  3. В нашем расширении вызовите метод, который мы на самом деле хотим вызвать с длинным массивом, передав наш упакованный массив:

    Object result = method.invoke(obj, new Object[] { bArray });
    
0 голосов
/ 30 ноября 2011

Длина строки ограничена Integer.MAX_VALUE

0 голосов
/ 30 ноября 2011

, если типом является число max length = Integer.MAX_VALUE, если типом является max max length = 65536

...