Как я могу определить, почему Hotspot JVM решил повторно скомпилировать уже JIT: ed code во второй раз?

Я пытаюсь написать процедуру разминки для чувствительного к задержкам приложения Java, чтобы оптимизировать первые несколько транзакций, которые в противном случае были бы замедлены динамической загрузкой классов и JIT (главным образом).

Проблема, с которой я сталкиваюсь, заключается в том, что хотя мой код разгрузки загружает все классы и выполняет их, вызывая их много раз (по крайней мере 100 раз -XX: CompileThreshold), позже, когда фактический пользователь регистрируется на этих же функциях помечен как "non entrant" и снова скомпилирован, что вызывает латентность.

Флаги JVM следующие (я добавил только -XX: + PrintCompilation -verbose: устранить неполадки класса tp, остальные - устаревшие):

-Xms5g -Xmx5g -server -XX: + AggressiveHeap -XX: + UseFastAccessorMethods -XX: + PrintGCDetails -XX: CompileThreshold = 100 -XX: -CITime -XX: -PrintGC -XX: -PrintGCTimeStamps -XX: + PrintCompilation -verbose: класс

#Warmup happens here
  12893 2351       my.test.application.hotSpot (355 bytes)
#Real user logs on here
 149755 2351      made not entrant  my.test.application.hotSpot (355 bytes)
 151913 2837       my.test.application.hotSpot (355 bytes)
 152079 2351      made zombie  my.test.application.hotSpot (355 bytes)

Никакая загрузка классов не происходит после разминки (я вижу загрузку класса, прежде чем он работает).

Похоже, что функция получает новый ID (2351 против 2837), что означает, что JVM как-то считается "другим".

И как я могу определить, почему JVM решил перекомпилировать эту функцию?

И я думаю, это сводится к тому, как я могу определить, почему изменился идентификатор? Каковы критерии?

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

Это JRE 1.6.0_45-b06.

Любые советы по устранению неполадок или получения дополнительной информации!:)

Ответы

Ответ 1

Для потомков это было довольно просто, как только я прочитал некоторый исходный код JVM hotspot.

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

-XX:+TraceDeoptimization -XX:+WizardMode -XX:+PrintNativeNMethods -XX:+PrintDependencies -XX:+DebugDeoptimization -XX:+LogEvents

Обычно это был оператор if.

void function (Object object){
    if ( object == null ){
        // do some uncommon cleanup or initialization
    }
    do_stuff();
}

Скажем, мой код разминки никогда не запускал оператор if.

Я предположил, что вся функция будет скомпилирована за один раз, однако, когда компилятор JIT C2 фактически решил создать собственный код для этой функции, он не будет генерировать код для оператора if, поскольку этот путь кода никогда не принимался.

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

В любом случае, если утверждение всегда истинно (т.е. объект всегда имеет значение null), тогда функция немедленно и безоговорочно инициирует эту обработку исключений и будет повторно скомпилирована (что приведет к хищению заморозки/задержки в порядке пары мс).

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

Это означает, что для эффективного разогрева приложения Java каждый отдельный if-оператор в коде должен быть вызван по развороту кода.

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

По следующим причинам мы собираемся перезаписать части приложения для поддержки работы в течение нескольких недель/месяцев за раз:

  • Простое обслуживание (нам не нужно моделировать производство во время разминки и обновлять его).
  • JIT не будет выполняться в соответствии с нашими пластическими симуляциями, но вместо этого будет работать с производством (т.е. использовать JIT для того, для чего он был разработан вместо того, чтобы бороться с ним).

Долгосрочный клиент, скорее всего, заплатит за переписывание на C/С++ или подобное, чтобы получить постоянную низкую задержку, но в течение другого дня.

EDIT: Позвольте мне добавить, что обновление до более новой версии JVM Hotspot или "настройка" вокруг параметров JVM точки доступа никогда не решит эту проблему. Они оба - дымы и зеркала. Дело в том, что JVM Hotspot никогда не был написан для предсказуемой малой задержки, и этот недостаток невозможно обойти из пользовательского пространства java.