Переформулируем вопрос:
Может ли компилятор 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