Почему это превышает ограничение в 65 535 байт в конструкторах Java и статических инициализаторах?

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

Я пробовал следующее как статическое поле, как поле экземпляра и инициализировал его непосредственно в конструкторе. Каждый раз, когда eclipse сообщает мне, что либо «Код конструктора TestData() превышает ограничение в 65535 байт», либо «Код статического инициализатора превышает ограничение в 65535 байт».

Есть 10000 целых чисел. Если каждое целое число составляет 4 байта (32 бита), то не будет ли это 40 000 байт? Действительно ли существует более 25 000 байт служебных данных в дополнение к данным, просто создающим массив?

Данные генерируются с помощью этого небольшого фрагмента Python:

#!/usr/bin/python

import random;
print "public final int[] RANDOM_INTEGERS = new int[] {";
for i in range(1,10000):
    print str(int(random.uniform(0,0x7fffffff))) + ",";
print "};";

Вот небольшой образец:

public final int[] RANDOM_INTEGERS = new int[] {
    963056418, 460816633, 1426956928, 1836901854, 334443802, 721185237, 488810483,
    1734703787, 1858674527, 112552804, 1467830977, 1533524842, 1140643114, 1452361499,
    716999590, 652029167, 1448309605, 1111915190, 1032718128, 1194366355, 112834025,
    419247979, 944166634, 205228045, 1920916263, 1102820742, 1504720637, 757008315,
    67604636, 1686232265, 597601176, 1090143513, 205960256, 1611222388, 1997832237,
    1429883982, 1693885243, 1987916675, 159802771, 1092244159, 1224816153, 1675311441,
    1873372604, 1787757434, 1347615328, 1868311855, 1401477617, 508641277, 1352501377,
    1442984254, 1468392589, 1059757519, 1898445041, 1368044543, 513517087, 99625132,
    1291863875, 654253390, 169170318, 2117466849, 1711924068, 564675178, 208741732,
    1095240821, 1993892374, 87422510, 1651783681, 1536657700, 1039420228, 674134447,
    1083424612, 2137469237, 1294104182, 964677542, 1506442822, 1521039575, 64073383,
    929517073, 206993014, 466196357, 1139633501, 1692533218, 1934476545, 2066226407,
    550646675, 624977767, 1494512072, 1230119126, 1956454185, 1321128794, 2099617717,
    //.... to 10,0000 instances

person Mark Renouf    schedule 25.04.2009    source источник
comment
Читая об этом, я обнаружил, что это ограничение применяется ко всем методам (включая конструкторы), а также к статическим инициализаторам. Интересно!   -  person Mark Renouf    schedule 25.04.2009
comment
groups.google.com/group/comp.lang. java.machine/browse_thread/   -  person Mark Renouf    schedule 25.04.2009


Ответы (6)


Вот байт-код для инициализации массива {1000001, 1000002, 1000003}:

 5  iconst_3
 6  newarray int [10]
 8  dup
 9  iconst_0
10  ldc <Integer 1000001> [12]
12  iastore
13  dup
14  iconst_1
15  ldc <Integer 1000002> [13]
17  iastore
18  dup
19  iconst_2
20  ldc <Integer 1000003> [14]
22  iastore
23  putfield net.jstuber.test.TestArrayInitializingConstructor.data : int[] [15]

Таким образом, для этого небольшого массива каждый элемент требует 5 байтов байт-кода Java. Для вашего большего массива как индекс массива, так и индекс в пуле констант будут использовать 3 байта для большинства элементов, что приводит к 8 байтам на элемент массива. Таким образом, для 10000 элементов вам придется ожидать около 80 КБ байтового кода.

Код для инициализации больших массивов с 16-битными индексами выглядит так:

2016  dup
2017  sipush 298
2020  ldc_w <Integer 100298> [310]
2023  iastore
2024  dup
2025  sipush 299
2028  ldc_w <Integer 100299> [311]
person starblue    schedule 25.04.2009
comment
Обрабатываются ли строковые литералы особым образом? Они единственное, что есть? Имеет ли смысл инициализировать массивы, упаковывая данные в строковые литералы [возможно, говоря, что для значений int числа +/-16383 хранятся как один символ, +/- 268435455 или некоторые другие значения выбора хранятся как два, а все остальное как три]? - person supercat; 19.03.2014
comment
@supercat Да, это может сработать. Каждая строка использует два постоянных индекса пула, один для строки (docs.oracle.com/javase/specs/jvms/se7/html/) и один для фактических данных UTF-8 (docs.oracle.com/javase/specs/jvms/se7/html/< /а>). Насколько я вижу, не существует другого типа констант произвольного размера (см. 4.4 Пул констант docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4). Хотя я бы предпочел использовать ресурс, чем такой хак. - person starblue; 19.03.2014

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

Почему бы не переместить эти данные в ресурс, который вы загружаете во время загрузки класса в статическом блоке инициализатора? Это легко сделать с помощью MyClass.class.getClassLoader().getResourceAsStream(). Кажется, что это там, где это должно быть, во всяком случае.

Или, что еще лучше, создайте случайные значения в статическом блоке инициализатора, используя доступные инструменты Java. И если вам нужны повторяющиеся «случайные» числа, просто заполните экземпляр Random фиксированным, но случайно выбранным числом каждый раз.

person Joachim Sauer    schedule 25.04.2009
comment
Тестируемая среда не разрешает доступ к файловому вводу-выводу. - person Mark Renouf; 25.04.2009
comment
Но вы загружаете классы, поэтому вы, вероятно, можете сделать MyClass.class.getResourceAsStream() и загрузить его из jar-файла, в который вы упаковываете свое приложение. Это всегда должно быть возможно. - person Joachim Sauer; 25.04.2009

Помимо значений целых чисел, конструктор и инициализатор должны содержать инструкции JVM для загрузки целых чисел в массив.

person TrayMan    schedule 25.04.2009
comment
Так что я думаю, это то, что я ожидал, просто удивительно, что литеральный код инициализации массива составляет › 25 000 байт (я уверен, что в настройке класса/метода/и т. д. есть небольшие накладные расходы). - person Mark Renouf; 25.04.2009
comment
Вы можете использовать javap, чтобы увидеть, что происходит. - person TrayMan; 25.04.2009

Гораздо проще и практичнее хранить числа в файле либо в двоичном формате, либо в виде текста.

Я не знаю, что java инициализирует массивы таким образом, но он не эффективно инициализирует большие массивы.

person Peter Lawrey    schedule 25.04.2009

Я думаю, что размер кода в символах больше, чем 65535. Не память, занимаемая 10000 целых чисел.

person Kamarey    schedule 25.04.2009
comment
11 символов, умноженные на 10 000 записей, составляют 110 000 байт, более или менее. Абсолютно сверх лимита - person Thomas Jones-Low; 25.04.2009
comment
Почему за этот ответ проголосовали? Это правильно, в минималистском стиле. - person Alan Moore; 25.04.2009

Я думаю, возможно, что это объем памяти, необходимый для буквенно-цифрового представления этих целых чисел. Я думаю, что это ограничение может применяться к самому коду, поэтому каждое целое число, например: 1494512072, фактически занимает 10 байтов (по одному на цифру) вместо 4 байтов, используемых для int32.

person arodrigues    schedule 25.04.2009
comment
Я совершенно уверен, что код, на который ссылается сообщение об ошибке, относится к сгенерированному байт-коду. - person Mark Renouf; 25.04.2009