Pull to refresh

Comments 20

Я немного не понимаю, если «автор вопроса приходит к числу 8191», то почему у нас получилось ~20000? Кто-то не прав?
Теоретически — до 21_845 элементов максимум, на практике это число ещё меньше

С чего вы взяли что около 20000? 8192 это все же до 21845, так что в принципе друг другу эти заключения не противоречат

Не противоречат. Просто человек вдался в такие подробности, и дошел до <21845, и остановился, но при этом он знает, что есть какие-то данные о более точном числе — 8191. Это странно как-то же. Надо было рассмотреть те аргументы, и уточнить еще больше, если это возможно.

21_845 это оценка сверху при следующих допущениях:


  • Для полной инициализации одного элемента перечисления нужна только инструкции putstatic
  • Более выгодного по размеру кода способа инициализировать static final поле нет

Первое утверждение неверно, как и указано в статье:


На самом деле эта оценка завышена. Значение для записи в поле инструкция берёт с вершины стека, которое туда поместила одна из предыдущих инструкций. И эта инструкция тоже занимает драгоценные байты.

Как дело обстоит со вторым пунктом сейчас и в вероятном будущем — узнаете из заключительной статьи цикла.


Число 8_191 у автора твита получилось после дополнительных оптимизаций, которые не делает javac.

Наверное, об этом будет часть 2

Судя по листингу, на инициализацию одного поля уходит 13 байт инструкций и ещё 6 байт на заполнение массива. Поэтому больше 3500 значений не должно быть, если только компилятор не генерирует другой код для больших перечислений.

Зависимость от количества элементов не совсем линейна, небольшой спойлер из второй части:


javac из OpenJDK 14-ea+29 сдаётся намного раньше, на 2_746 элементах
Интересное исследование =) Хотя кому на практике понадобится 20к значений в ENUM?

всяким кодогенераторам в каких-то уж очень вычурных реализациях?

Мне однажды понадобилось. И да — кодегенератор (метаинформация по библиотеки иконек). В моём случае, количество констант оказалось ограниченно именно размером сгенерёного класса.
В сях, в различных драйверах и поболее встречалось.

После установления точной границы интересное предложение исследования — сделать хотя бы один свитч по этому енаму. Хоть бы и пустой. Сработает? Или придётся лимит ещё снижать?

Для каждого свича делать свой анонимный класс)
А вместо кода в ветках вызывать методы родительского класса — их вполне может быть 8к.

Один switch со всеми элементами?
Так-то можно обрабатывать фрагментами, помещая в ветку default вызов метода с обработкой следующего фрагмента.


С ходу можно прикинуть:


Пропусков у ordinal() нет, заголовок у tableswitch занимает ~16 байтов, дальше — по 4 байта на каждый вариант. Плюс, как минимум, aload_<n> и invokevirtual OurEnum::ordinal() в начале и return в конце.


(65_535 - 16 - 1 - 1 - 3) / 4 ~ 16_378 элементов, дальше будет Code too large.
Интрига в том, сможем ли мы дойти до этого числа.

Нет-нет, один пустой свитч :-) А чего ты гадаешь вообще? Кодогенератор тривиально написать и установить граница экспериментально.

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

В 2017 году мне тоже был интересен этот вопрос, тогда я решил задачу в лоб с помощью кодогенерации и проверил эмпирически для java7. Статья на хабре здесь — ответ в секции Эксперимент. У меня получилось
столько
2746

Убрал под спойлер из уважения к автору этой статьи, так как он обещает продолжение

На уровне javac со времён Java 7 в компиляции перечислений ничего не изменилось, javac из OpenJDK 14 по-прежнему поддерживает не больше
2_746 элементов в перечислении.


Тем интереснее искать теоретически достижимый максимум.

UFO just landed and posted this here
Sign up to leave a comment.

Articles