Ответ 1
Одна из проблем заключается в том, что "то, что JVM на самом деле делало с ней", изменяется между вызовами, поскольку JVM может свободно генерировать код.
В качестве примера я несколько дней назад исследовал, что Hotspot делает с методами final
по сравнению с виртуальными методами. Судя по микрообъектам, мои выводы были следующими:
-
Клиент JVM: если метод эффективен
final
(нет переопределенного класса загрузки), JVM использует невиртуальный вызов. Впоследствии, если вы загружаете класс, который переопределяет этот метод, JVM изменит код JIT, чтобы сделать виртуальные вызовы. Поэтому объявлениеfinal
не имеет существенной значимости. -
Сервер JVM: здесь
final
, похоже, тоже не имеет отношения. Похоже, что JVM генерирует не виртуальный вызов для любого класса, который вы используете в первый раз, независимо от того, какие классы загружены. Впоследствии, если вы сделаете вызов с объекта другого класса, JVM будет исправлять все вызовы с чем-то похожим на это (я думаю, что он будет также выполнять профильные вызовы, чтобы он мог изменять быстрый и медленный путь, если он не получил это правильно в первый раз):
if (object instanceof ClassOfNonVirtualCall) { do non-virtual call to ClassOfNonVirtualCall.method } else { do virtual call to object.method }
Если вам действительно интересно увидеть сгенерированный код, вы можете играть с JVM из DEBUG из OpenJDK: