Как JVM решила JIT-скомпилировать метод (классифицировать метод как "горячий")?

Я уже работал с -XX:+PrintCompilationи я знаю основные приемы JIT-компилятора и почему используется JIT-компиляция.

Тем не менее, я до сих пор не выяснил, как JVM решает JIT-компилировать метод, то есть "когда пришло подходящее время для JIT-компиляции метода".

Прав ли я в предположении, что каждый метод начинает интерпретироваться, и пока он не отнесен к категории "горячий метод", он не будет скомпилирован? У меня есть что-то в затылке, что я прочитал, что метод считается "горячим", когда он был выполнен как минимум 10.000 раз (после интерпретации метода 10.000 раз он будет скомпилирован), но я должен признать, что я не уверен в этом или где я читал это.

Итак, чтобы подвести итог моего вопроса:

(1) Каждый метод интерпретируется до тех пор, пока он не был классифицирован как "горячий" метод (и, следовательно, был скомпилирован), или есть ли причины для методов, которые компилируются, даже если они не "горячие"?

(2) Как JVM разделяет методы на "не горячие" и "горячие" методы? Номер исполнения? Что-нибудь еще?

(3) Если для "горячих" методов существуют определенные пороги (например, количество выполнений), существуют ли флаги Java (-XX:...) чтобы установить эти пороги?

2 ответа

Решение

Политика компиляции HotSpot довольно сложная, особенно для Tiered Compilation, которая включена по умолчанию в Java 8. Это не количество выполнений и не вопрос CompileThreshold параметр.

Лучшее объяснение (по-видимому, единственное разумное объяснение) можно найти в источниках HotSpot, см. AdvancedThresholdPolicy.hpp.

Я кратко изложу основные положения этой расширенной политики компиляции:

  • Выполнение начинается с уровня 0 (переводчик).
  • Основными триггерами для компиляции являются
    1. счетчик вызовов методов i;
    2. счетчик задников b, Обратные ветви обычно обозначают цикл в коде.
  • Каждый раз, когда счетчики достигают определенного значения частоты (TierXInvokeNotifyFreqLog, TierXBackedgeNotifyFreqLog), политика компиляции вызывается, чтобы решить, что делать дальше с текущим запущенным методом. В зависимости от значений i, b и текущую нагрузку потоков компилятора C1 и C2 можно решить

    • продолжить исполнение в переводчике;
    • начать профилирование в интерпретаторе;
    • метод компиляции с C1 на уровне 3 с полными данными профиля, необходимыми для дальнейшей перекомпиляции;
    • метод компиляции с C1 на уровне 2 без профиля, но с возможностью перекомпиляции (маловероятно);
    • наконец, скомпилируйте метод с C1 на уровне 1 без профиля или счетчиков (также маловероятно).

    Основные параметры здесь TierXInvocationThreshold а также TierXBackEdgeThreshold, Пороговые значения могут динамически настраиваться для данного метода в зависимости от длины очереди компиляции.

  • Очередь компиляции - это не FIFO, а очередь с приоритетами.

  • Скомпилированный С1 код с данными профиля (уровень 3) ведет себя аналогично, за исключением того, что пороговые значения для перехода на следующий уровень (уровень 2, С2) намного больше. Например, интерпретированный метод может быть скомпилирован на уровне 3 после примерно 200 вызовов, в то время как метод, скомпилированный C1, подлежит перекомпиляции на уровне 4 после вызовов 5000+.

  • Специальная политика используется для встраивания метода. Крошечные методы могут быть встроены в вызывающего, даже если они не "горячие". Немного большие методы могут быть встроены, только если они вызываются часто (InlineFrequencyRatio, InlineFrequencyCount).

Основным параметром для управления этим является -XX:CompileThreshold=10000

Hotspot для Java 8 теперь использует многоуровневую компиляцию по умолчанию, используя несколько этапов компиляции от уровня 1 до 4. Я считаю, что 1 не является оптимизацией. Уровень 3 - это C1 (на основе клиентского клиента), а уровень 4 - это C2 (на основе серверного компилятора)

Это означает, что небольшая оптимизация может произойти раньше, чем вы ожидаете, и она может продолжать оптимизировать даже после достижения порога в 10 КБ. Максимум, который я видел, - это анализ побега, исключающий StringBuilder после миллиона вызовов.

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

1) Пока метод не считается достаточно горячим, он интерпретируется. Однако некоторые JVM (например, Azul Zing) могут компилировать методы при запуске, и вы можете заставить JVM Hotspot компилировать метод через внутренний API. Java 9 также может иметь компилятор AOT (Ahead Of Time), но он все еще исследуется AFAIK

2) Количество вызовов или количество итераций.

3) да -XX:CompileThreshold= быть главным.

Другие вопросы по тегам