Размер строки инициализации в java

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

Спасибо

изменить

Мы создаем строку инициализации, которая будет выглядеть примерно так: "{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 });

}

Ответ 1

Длина строкового литерала (т.е. "...") ограничена структурой файла 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.

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

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

    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 делая окончательный arraycopy. Это эскиз, таким образом, это самая простая реализация.)

  • Из нашего расширения создайте объект этого класса, и добавьте элементы к этому объекту, вызвав его метод .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 можно было бы получить один раз и кэшировать вместо одного раза для каждого преобразования массива.)
    Если вы создаете содержимое массивов по некоторому алгоритму, вы можете сделать это поколение здесь, а не сначала создать другой объект-обертку.

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

    Object result = method.invoke(obj, new Object[] { bArray });
    

Ответ 3

если type is number max length = Integer.MAX_VALUE, если тип char max length = 65536