Давайте начнем с двух простых классов:
package com.michaelt.so.supers;
public class Sup {
int methodA(int a, int b) {
return a + b;
}
}
а потом
package com.michaelt.so.supers;
public class Sub extends Sup {
@Override
int methodA(int a, int b) {
return super.methodA(a, b);
}
}
Компилируя methodA и глядя на байт-код, мы получаем:
methodA(II)I
L0
LINENUMBER 6 L0
ALOAD 0
ILOAD 1
ILOAD 2
INVOKESPECIAL com/michaelt/so/supers/Sup.methodA (II)I
IRETURN
L1
LOCALVARIABLE this Lcom/michaelt/so/supers/Sub; L0 L1 0
LOCALVARIABLE a I L0 L1 1
LOCALVARIABLE b I L0 L1 2
MAXSTACK = 3
MAXLOCALS = 3
И вы можете сразу увидеть с помощью метода invokespecial, который выполняет поиск в методе класса SupA ().
Код операции invokespecial имеет следующую логику:
- Если C содержит объявление для метода экземпляра с тем же именем и дескриптором, что и у разрешенного метода, то этот метод будет вызван. Процедура поиска завершается.
- В противном случае, если C имеет суперкласс, эта же процедура поиска выполняется рекурсивно с использованием прямого суперкласса C. Метод, который должен быть вызван, является результатом рекурсивного вызова этой процедуры поиска.
- В противном случае выдается AbstractMethodError.
В этом случае не существует метода экземпляра с тем же именем и дескриптором в его классе, поэтому первая пуля не сработает. Вторая пуля, однако, будет - есть суперкласс, и он вызывает метод суперА.
Компилятор этого не делает, и в классе нет копии источника Sup.
Однако история еще не закончена. Это просто скомпилированный код. Как только код попадет в JVM, HotSpot может подключиться.
К сожалению, я не очень много знаю об этом, поэтому я обращусь к авторитету по этому вопросу и перейду к Inlining in Java, где говорится, что HotSpot может использовать встроенные методы (даже не финальные).
Переходя к документам, отмечается, что если конкретный вызов метода становится горячей точкой вместо того, чтобы выполнять этот поиск каждый раз, эта информация может быть встроена - эффективно копируя код из Sup methodA () в Sub methodA ().
Это делается во время выполнения, в памяти, в зависимости от поведения приложения и оптимизации, необходимой для повышения производительности.
Как указано в HotSpot Internals for OpenJDK, «методы часто являются встроенными. Статические, частные, окончательные и / или« специальные »вызовы легко встроить».
Если вы покопаетесь в опциях для JVM, вы найдете опцию -XX:MaxInlineSize=35
(35 по умолчанию), которая является максимальным числом байтов, которое может быть встроено. Я укажу, что именно поэтому Java любит иметь много маленьких методов - потому что они могут быть легко встроены. Эти маленькие методы становятся быстрее, когда их вызывают больше, потому что они могут быть встроены. И хотя с этим числом можно играть и увеличивать его, другие оптимизации могут быть менее эффективными. (связанный вопрос SO: стратегия встраивания HotSpot JIT, которая указывает на ряд других вариантов, чтобы взглянуть на внутреннюю часть встраивания, которую делает HotSpot).
Итак, нет - код не встроен во время компиляции. И да - код вполне может быть встроен во время выполнения, если этого требует оптимизация производительности.
И все, что я пишу о встраивании HotSpot, относится только к HotSpot JVM, распространяемой Oracle. Если вы посмотрите на список виртуальных машин Java в Википедии, существует гораздо больше, чем просто HotSpot, и способ, которым эти JVM обрабатывают встраивание, может полностью отличаться от того, что я описал выше. Apache Harmony, Dalvik, ART - там все может работать по-другому.