Почему код JIT-компиляции JVM не кэшируется?

107

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

Вопрос в том, почему этот скомпилированный код не кэшируется на диск для последующего использования одной и той же функции / класса?

В его нынешнем виде каждый раз, когда выполняется программа, JIT-компилятор запускается заново, а не использует предварительно скомпилированную версию кода. Разве добавление этой функции не привело бы к значительному увеличению начального времени выполнения программы, когда байт-код, по сути, интерпретируется?

Чинмай Канчи
источник
4
Тема,
miku
2
Но маловероятный вопрос, требующий однозначного ответа.
bmargulies 02
1
Я не уверен в «значительном» повышении, потому что тогда вам придется загружать JITted-данные с диска, а не JIT-файлы в памяти. Это может ускорить процесс, но в каждом конкретном случае.
R. Martinho Fernandes
1
Спасибо всем за отличные ответы! Все ответы были одинаково верными, поэтому я
согласился
Это хороший вопрос, если вы спросите меня :)
Альфред

Ответы:

25

Не прибегая к сокращению ссылки, которую опубликовал @MYYN, я подозреваю, что это связано с тем, что оптимизация, которую выполняет JVM, не статическая, а скорее динамическая, на основе шаблонов данных, а также шаблонов кода. Вероятно, что эти шаблоны данных изменятся в течение жизненного цикла приложения, в результате чего кэшированные оптимизации будут менее оптимальными.

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

Скаффман
источник
6
... или вы можете просто предложить постоянство в качестве опции , как это делает Oracle JVM - дать опытным программистам возможность оптимизировать производительность своих приложений, когда и где они просто знают, что шаблоны не меняются, под их ответственность. Почему нет?!
Alex Martelli
2
Потому что, наверное, не стоит. Если ни SUN, ни IBM, ни BEA не сочли это целесообразным с точки зрения производительности JVM, для этого будет веская причина. Возможно, их оптимизация RT выполняется быстрее, чем у Oracle, поэтому Oracle кэширует ее.
skaffman
9
Почему бы не взять сохраненные оптимизации в качестве отправной точки, чтобы использовать то, что было изучено в ходе предыдущих прогонов? Оттуда JIT может работать как обычно, чтобы повторно оптимизировать материал. При выключении этот код можно было снова сохранить и использовать в следующем запуске как новую отправную точку.
Puce
1
@Puce Единственная причина, по которой я могу думать, это то, что, AFAIK, вы не получаете статистики профилирования от запуска оптимизированного кода. Так что у вас не будет возможности стать лучше ...
maaartinus
1
Я лично был бы в порядке с опцией «просто сохранять информацию профилирования JIT между запусками» со всеми предупреждениями о том, что «это будет действительным только с точно такой же JVM, теми же данными и т. Д. И в противном случае игнорируется». Что касается того, почему это не было реализовано, я ожидал, что дополнительная сложность сохранения и проверки исходных данных JIT была слишком большой, чтобы брать ресурсы из других проектов. Учитывая выбор между этим потоком и потоками лямбда + Java 8, я бы предпочел последний.
Торбьорн Равн Андерсен
25

JVM Oracle действительно документирована для этого - цитируя Oracle,

компилятор может использовать модель разрешения классов Oracle JVM, чтобы при желании сохранять скомпилированные методы Java между вызовами, сеансами или экземплярами базы данных. Такое постоянство позволяет избежать накладных расходов, связанных с ненужной перекомпиляцией между сеансами или экземплярами, когда известно, что семантически код Java не изменился.

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

Алекс Мартелли
источник
8
Потому что у других сложных JVM нет отличной корпоративной СУБД, удобной для хранения информации :)
skaffman
Вот Это Да! это означает, что компиляции время от времени кэшируются. Это хорошие новости!
Сандип Джиндал,
Для этого также задокументирован IBM J9.
user314104
9
Обратите внимание, что эта Oracle JVM находится внутри Oracle Database, а не загружается Oracle при покупке Sun.
Thorbjørn Ravn Andersen
14

Обновление существующих ответов - в Java 8 есть JEP, посвященный решению этой проблемы:

=> JEP 145: Кэш-скомпилированный код . Новая ссылка .

На очень высоком уровне заявленная цель :

Сохраняйте и повторно используйте скомпилированный машинный код из предыдущих запусков, чтобы сократить время запуска больших Java-приложений.

Надеюсь это поможет.

Евгений
источник
3
Эта функция еще не дошла до финальной версии .
assylias
5

Excelsior JET имеет кэширующий JIT-компилятор, начиная с версии 2.0, выпущенной еще в 2001 году. Более того, его компилятор AOT может перекомпилировать кеш в одну DLL / общий объект, используя все оптимизации.

Дмитрий Лесков
источник
3
Да, но вопрос касался канонической JVM, то есть JVM Sun. Мне хорошо известно, что существует несколько компиляторов AOT для Java, а также другие кэширующие JVM.
Chinmay Kanchi
0

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

  • Идея Java состоит в том, чтобы быть языком с возможностью однократной записи и запуском в любом месте, и размещение предварительно скомпилированного материала в файле класса является своего рода нарушением этого (только «своего рода», потому что, конечно, фактический байтовый код все еще будет там)
  • Это увеличило бы размеры файлов классов, потому что у вас был бы один и тот же код там несколько раз, особенно если вы запускаете одну и ту же программу на нескольких разных JVM (что на самом деле не редкость, если вы считаете разные версии разными JVM, которые вы действительно нужно сделать)
  • Сами файлы классов могут быть недоступны для записи (хотя это довольно легко проверить)
  • Оптимизация JVM частично основана на информации о времени выполнения, а при других запусках они могут быть неприменимы (хотя они все равно должны давать некоторые преимущества)

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

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

JaakkoK
источник
1
в файле классов есть место для таких вещей, фактически это было первоначальное намерение (сохранить JIT-код как атрибут в файле классов).
TofuBeer 02
@TofuBeer: Спасибо за подтверждение. Я подозревал, что это может быть так (я бы так и поступил), но не был уверен. Отредактировано, чтобы удалить это как возможную причину.
JaakkoK 02
Я думаю, ты попал в точку с последней пулей. Остальные можно обойти, но я думаю, что последняя часть является основной причиной того, что JIT-код не сохраняется.
Саша Чедыгов 02
1
Последний абзац о явном компиляторе преобразования байт-кода в собственный - это то, что у вас есть в .NET с NGEN ( msdn.microsoft.com/en-us/library/6t9t5wcf(VS.71).aspx ).
Р. Мартиньо Фернандес