Путаница с HotSpot JVM JIT

Например, цикл с 10000 раз в методе. Когда он выполняется 1000 раз, backedge_counter запускает компиляцию JIT. И интерпретатор продолжает выполнение. Когда он зациклится 4000 раз, компиляция JIT завершится.

У меня вопрос: как оставшиеся 6000 раз выполняются интерпретатором или машинным кодом? Или собственный код не выполняется до тех пор, пока этот метод не будет вызван в следующий раз? И что произойдет, когда этот метод будет вызван в следующий раз?


person Bolen.Zhang    schedule 20.08.2017    source источник
comment
Почему бы ему не выполняться изначально? Разве не в этом смысл JIT-компиляции?   -  person shmosel    schedule 20.08.2017
comment
+1 Это интересный, нетривиальный вопрос, который ИМХО не заслуживает отрицательных голосов. Может ли компилятор Java HotSpot изменить текущий вызов метода с интерпретируемого на скомпилированный код? Формулировка вопроса не идеальна, поскольку подходы JIT и HotSpot слегка различаются, но из текста ясно, что имеется в виду HotSpot.   -  person Ralf Kleberhoff    schedule 20.08.2017
comment
@RalfKleberhoff Если бы он не мог изменить метод, в чем именно был бы смысл? Возможно, это позволит завершиться текущему вызову, но, конечно, не следующие 5999 раз. Вопрос глупый.   -  person user207421    schedule 20.08.2017
comment
Конечно, HotSpot может менять методы с интерпретируемых на нативные, пока этот метод не выполняется. Это само собой разумеющееся. Это также возможно, пока метод находится в середине своего выполнения?   -  person Ralf Kleberhoff    schedule 20.08.2017
comment
@RalfKleberhoff Это не тот вопрос, который был задан. Вопрос явно касался оставшихся 6000 вызовов.   -  person user207421    schedule 20.08.2017
comment
@ EJP, извините за неясное выражение. я изменяю его   -  person Bolen.Zhang    schedule 20.08.2017


Ответы (2)


Предполагая, что вы спрашиваете о HotSpot JVM, ответ заключается в том, что оставшиеся взаимодействия будут выполняться в скомпилированном коде.

В HotSpot JVM есть метод, известный как «замена в стеке», позволяющий переключаться с интерпретатора на скомпилированный код во время выполнения метода.

http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html

замена в стеке
Также известна как OSR. Процесс преобразования интерпретированного (или менее оптимизированного) кадра стека в скомпилированный (или более оптимизированный) кадр стека. Это происходит, когда интерпретатор обнаруживает, что метод зациклен, запрашивает компилятор сгенерировать специальный n-метод с точкой входа где-то в цикле (в частности, в обратной ветви) и передает управление этому n-методу. Грубая противоположность деоптимизации.

Если вы запускаете JVM с флагом -XX:+PrintCompilation, компиляции OSR будут отмечены знаком %:

    274   27       3       java.lang.String::lastIndexOf (52 bytes)
    275   29       3       java.lang.String::startsWith (72 bytes)
    275   28       3       java.lang.String::startsWith (7 bytes)
    275   30       3       java.util.Arrays::copyOf (19 bytes)
    276   32       4       java.lang.AbstractStringBuilder::append (29 bytes)
    276   31  s    3       java.lang.StringBuffer::append (13 bytes)
    283   33 %     3       LoopTest::myLongLoop @ 13 (43 bytes)
             ^                                    ^
            OSR                            bytecode index of OSR entry

ОБНОВЛЕНИЕ

Обычно после компиляции OSR обычная компиляция также ставится в очередь, так что при следующем вызове метода он запустится непосредственно в скомпилированном режиме.

    187   32 %     3       LoopTest::myLongLoop @ 13 (43 bytes)
    187   33       3       LoopTest::myLongLoop (43 bytes)

Однако, если обычная компиляция не будет завершена к моменту повторного вызова метода, метод запустится в интерпретаторе, а затем переключится на запись OSR внутри цикла.

person apangin    schedule 20.08.2017
comment
Но что произойдет, когда этот метод будет вызван в следующий раз? - person Bolen.Zhang; 20.08.2017
comment
Ваш ответ очень полезен. - person Bolen.Zhang; 20.08.2017

Переформулируем вопрос:

Может ли компилятор Java HotSpot изменить метод с интерпретируемого на скомпилированный в середине его выполнения?

Я думаю, что может.

Задача непростая для движка (некоторый опыт в этой области я накопил, работая над компилятором Ahead-of-Time под названием JUMP для карманных компьютеров с PalmOS несколько лет назад). Когда двигатель решает переключиться, он должен учитывать как минимум следующие моменты:

  • Где счетчик программ? В интерпретируемом коде он находится на некотором смещении байт-кода от начала метода, точно зная, какой байт-код выполнять следующим. В оптимизированном собственном коде байт-коды JVM обычно не транслируются в изолированные блоки машинных инструкций, а зависят друг от друга, перестраиваются не по порядку и т. д. Таким образом, может не быть собственного адреса инструкции, который точно соответствует программному счетчику байт-кода при переключении.

  • Где данные? Интерпретатор (вероятно) хранит все в стеке, оптимизированный нативный код использует смесь распределения регистров и стека, которые будут разными для разных мест в нативном переводе.

Итак, я прочитал белую книгу HotSpot. Он не отвечает на вопрос явно, но есть подсказка под «деоптимизацией». Когда новый класс загружается динамически или даже заменяется в сеансе отладки, предыдущие оптимизации, такие как встраивание, могут стать недействительными.

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

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

РЕДАКТИРОВАТЬ:

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

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

void loop() {
    for (int i=0; i<10000; i++) {
        // example loop body
        objects[i].doSomething();
    }
}

После, например. 4000 итераций компилятор HotSpot оптимизировал этот метод. Что происходит тогда?

Есть два аспекта, один тривиальный и один сложный:

  • Тривиальным является то, что вызовы, которые происходят внутри цикла (например, doSomething()), будут вызывать их скомпилированную версию, как только она станет доступной. Я не упомянул об этом в своем первоначальном ответе, так как считал это само собой разумеющимся.

  • Сложный аспект заключается в следующем: переключится ли текущее выполнение loop() с интерпретируемого на скомпилированный код при i=4000? Это то, что я понял как вопрос ОП.

person Ralf Kleberhoff    schedule 20.08.2017
comment
Давайте не переформулируем вопрос. Давайте ответим на вопрос, который был задан. Если у вас есть свой вопрос, задайте его, и если у вас есть свой ответ на свой вопрос, опубликуйте его там. Держаться ближе к делу. - person user207421; 20.08.2017
comment
Но что произойдет, когда этот метод будет вызван в следующий раз? - person Bolen.Zhang; 20.08.2017