Vamos começar com duas classes simples:
package com.michaelt.so.supers;
public class Sup {
int methodA(int a, int b) {
return a + b;
}
}
e depois
package com.michaelt.so.supers;
public class Sub extends Sup {
@Override
int methodA(int a, int b) {
return super.methodA(a, b);
}
}
Compilando methodA e observando o código de bytes que se obtém:
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
E você pode ver ali, com o método invokespecial, que faz a pesquisa no método A da classe Sup ().
O opcode invokepecial tem a seguinte lógica:
- Se C contiver uma declaração para um método de instância com o mesmo nome e descritor que o método resolvido, esse método será chamado. O procedimento de pesquisa termina.
- Caso contrário, se C tiver uma superclasse, esse mesmo procedimento de pesquisa será executado recursivamente usando a superclasse direta de C. O método a ser invocado é o resultado da invocação recursiva desse procedimento de pesquisa.
- Caso contrário, um AbstractMethodError é gerado.
Nesse caso, não há método de instância com o mesmo nome e descritor em sua classe, portanto o primeiro marcador não será acionado. A segunda bala, no entanto - haverá uma superclasse e ela invocará o método A da super.
O compilador não alinha isso e não há cópia da fonte de Sup na classe.
No entanto, a história ainda não está concluída. Este é apenas ocódigo compilado . Quando o código atinge a JVM, o HotSpot pode se envolver.
Infelizmente, eu não sei muito sobre isso, então apelo à autoridade sobre este assunto e vou para Inlining em Java, onde é dito que o HotSpot pode incorporar métodos (mesmo métodos não finais).
Indo para os documentos , observe que, se uma chamada de método específica se tornar um hot spot, em vez de fazer essa pesquisa a cada vez, essas informações poderão ser incorporadas - copiando efetivamente o código do método Sup (A) para Sub methodA ().
Isso é feito em tempo de execução, na memória, com base em como o aplicativo está se comportando e quais otimizações são necessárias para acelerar o desempenho.
Conforme declarado no HotSpot Internals for OpenJDK, "Métodos são frequentemente incorporados. Invocações estáticas, privadas, finais e / ou" especiais "são fáceis de incorporar".
Se você pesquisar nas opções da JVM , encontrará uma opção de -XX:MaxInlineSize=35
(35 sendo o padrão), que é o número máximo de bytes que podem ser incorporados. Vou salientar que é por isso que o Java gosta de ter muitos métodos pequenos - porque eles podem ser facilmente incorporados. Esses pequenos métodos se tornam mais rápidos quando são chamados mais porque podem ser incorporados. E, embora se possa brincar com esse número e aumentá-lo, isso pode fazer com que outras otimizações sejam menos eficazes. (questão SO relacionada: estratégia de inline do HotSpot JIT, que aponta várias outras opções para espiar os aspectos internos do inline que o HotSpot está fazendo).
Portanto, não - o código não está embutido no momento da compilação. E sim - o código pode muito bem ser incorporado em tempo de execução se as otimizações de desempenho o justificarem.
E tudo o que escrevi sobre o HotSpot embutido se aplica apenas ao HotSpot JVM distribuído pela Oracle. Se você olhar a lista de máquinas virtuais Java da wikipedia, existem muito mais do que apenas HotSpot e a maneira como essas JVMs lidam com inlining pode ser completamente diferente do que eu descrevi acima. Apache Harmony, Dalvik, ART - as coisas podem funcionar de maneira diferente lá.