Vamos olhar para um (progressivamente menos e menos) a descrição detalhada de cada classe de bytecodes.
Para cada bytecode, algum breve texto descreve a sua função e um "quadro" textual da pilha, tanto antes como depois que o bytecode se realizou, mostra-se. Este quadro de texto parecerá ao seguinte:
..., value1, value2 => ..., value3
Isto significa que o bytecode espera que dois operands-value1 e value2 - estejam no topo da pilha, põe de repente ambos eles da pilha, os produz para produzir value3 e empurra value3 atrás para o topo da pilha. Deve ler cada pilha no direito ao esquerdo, com o valor mais à direita que é o topo da pilha. O ... lê-se como "o resto da pilha abaixo", que é inaplicável à corrente bytecode. Todos os operands na pilha são 32 bits de largura.
Como a maior parte de bytecodes tomam os seus argumentos da pilha e colocam os seus resultados atrás lá, as breves descrições de texto que só seguem dizem algo sobre a fonte ou destino de valores se não estiverem na pilha. Por exemplo, a descrição "Load integer from local variable." significa que o número inteiro se carrega para a pilha e "Integer add." destina os seus números inteiros a tomar-se de - e o resultado voltou a - a pilha.
Bytecodes que não afetam o fluxo de controle simplesmente entram o pc para o seguinte bytecode que segue na sequência. Aqueles que realmente afetam o pc dizem tão explicitamente. Sempre que veja byte1, byte2, e assim por diante, refere-se ao primeiro byte, segundo byte, e assim por diante, que seguem o próprio byte de código de operação. Depois que tal bytecode realiza-se, o pc automaticamente avança sobre estes bytes operand para começar o seguinte bytecode na sequência.
Observar |
As seguintes poucas seções estão "no estilo de manual de referência", apresentando cada bytecode separadamente em total o seu (muitas vezes redundante) detalhe; cada bytecode apresenta-se como uma operação seguida de uma explicação. As seções posteriores começam a cair e coalescer este estilo verboso em algo mais curto e mais legível. A forma verbosa mostra-se no início porque os manuais de referência online olharão mais como ela, e porque dirige para casa o ponto que cada bytecode "função" vem para muitos, bytecodes quase idêntico, um para cada tipo primitivo em Java. |
bipush ... => ..., value
Empurre 1 byte assinou o número inteiro. byte1 interpreta-se como um valor de 8 bits assinado. Este value estende-se a um int e empurra-se para a pilha de operand.
sipush ... => ..., value
Empurre 2 bytes assinaram o número inteiro. byte1 e byte2 reúnem-se em um valor de 16 bits assinado. Este value estende-se a um int e empurra-se para a pilha de operand.
ldc1 ... => ..., item
Empurre item do consórcio constante. byte1 usa-se como um índice de 8 bits não assinado no consórcio constante da classe atual. O item naquele índice resolve-se e empurra-se para a pilha.
ldc2 ... => ..., item
Empurre item do consórcio constante. byte1 e byte2 usam-se para construir um índice de 16 bits não assinado no consórcio constante da classe atual. O item naquele índice resolve-se e empurra-se para a pilha.
ldc2w ... => ..., constant-word1, constant-word2
Empurre long ou double do consórcio constante. byte1 e byte2 usam-se para construir um índice de 16 bits não assinado no consórcio constante da classe atual. A constante de duas palavras naquele índice resolve-se e empurra-se para a pilha.
aconst_null ... => ..., null
Empurre a referência a objeto de null para a pilha.
iconst_m1 ... => ..., -1
Empurre o int -1 para a pilha.
iconst_<I> ... => ..., <I>
Empurre o int <I> para a pilha. Há seis destes bytecodes, um para cada um dos números inteiros 0-5: iconst_0, iconst_1, iconst_2, iconst_3, iconst_4 e iconst_5.
lconst_<L> ... => ..., <L>-word1, <L>-word2
Empurre o long <L> para a pilha. Há dois destes bytecodes, um para cada um dos números inteiros 0 e 1: lconst_0 e lconst_1.
fconst_<F> ... => ..., <F>
Empurre o float <F> para a pilha. Há três destes bytecodes, um para cada um dos números inteiros 0-2: fconst_0, fconst_1 e fconst_2.
dconst_<D> ... => ..., <D>-word1, <D>-word2
Empurre o double <D> para a pilha. Há dois destes bytecodes, um para cada um dos números inteiros 0 e 1: dconst_0 e dconst_1.
iload ... => ..., value
Carregue int da variável local. byte1 variável local na armação de Java atual deve conter um int. O value daquela variável empurra-se para a pilha de operand.
iload_<I> ... => ..., value
Carregue int da variável local. <I> variável local na armação de Java atual deve conter um int. O value daquela variável empurra-se para a pilha de operand. Há quatro destes bytecodes, um para cada um dos números inteiros 0-3: iload_0, iload_1, iload_2 e iload_3.
lload ... => ..., value-word1, value-word2
Carregue long da variável local. As variáveis locais byte1 e byte1 + 1 na armação de Java atual devem conter em conjunto um número inteiro longo. Os valores contidos naquelas variáveis empurram-se para a pilha de operand.
lload_<L> ... => ..., value-word1, value-word2
Carregue long da variável local. As variáveis locais <L> e <L> + 1 na armação de Java atual devem conter em conjunto um número inteiro longo. O valor contido naquelas variáveis empurra-se para a pilha de operand. Há quatro destes bytecodes, um para cada um dos números inteiros 0-3: lload_0, lload_1, lload_2 e lload_3.
fload ... => ..., value
Carregue float da variável local. byte1 variável local na armação de Java atual deve conter um número de ponto flutuante de precisão única. O value daquela variável empurra-se para a pilha de operand.
fload_<F> ... => ..., value
Carregue float da variável local. <F> variável local na armação de Java atual deve conter um número de ponto flutuante de precisão única. O valor daquela variável empurra-se para a pilha de operand. Há quatro destes bytecodes, um para cada um dos números inteiros 0-3: fload_0, fload_1, fload_2 e fload_3.
dload ... => ..., value-word1, value-word2
Carregue double da variável local. As variáveis locais byte1 e byte1 + 1 na armação de Java atual devem conter em conjunto um número de ponto flutuante de precisão dupla. O valor contido naquelas variáveis empurra-se para a pilha de operand.
dload_<D> ... => ..., value-word1, value-word2
Carregue double da variável local. As variáveis locais <D> e <D> + 1 na armação de Java atual devem conter em conjunto um número de ponto flutuante de precisão dupla. O valor contido naquelas variáveis empurra-se para a pilha de operand. Há quatro destes bytecodes, um para cada um dos números inteiros 0-3: dload_0, dload1, dload_2 e dload_3.
aload ... => ..., value
Referência a objeto de carga de variável local. byte1 variável local na armação de Java atual deve conter um endereço de regresso ou referência para um objeto ou tabela. O valor daquela variável empurra-se para a pilha de operand.
aload_<A> ... => ..., value
Referência a objeto de carga de variável local. <A> variável local na armação de Java atual deve conter um endereço de regresso ou referência para um objeto. O valor daquela variável empurra-se para a pilha de operand. Há quatro destes bytecodes, um para cada um dos números inteiros 0-3: aload_0, aload_1, aload_2 e aload_3.
istore ..., value => ...
Guarde int na variável local. value deve ser um int. byte1 variável local na armação de Java atual estabelece-se em value.
istore_<I> ..., value => ...
Guarde int na variável local. value deve ser um int. <I> variável local na armação de Java atual estabelece-se em value. Há quatro destes bytecodes, um para cada um dos números inteiros 0-3: istore_0, istore_1, istore_2 e istore_3.
lstore ..., value-word1, value-word2 => ...
Guarde long na variável local. value deve ser um número inteiro longo. As variáveis locais byte1 e byte1 + 1 na armação de Java atual estabelecem-se em value.
lstore_<L> ..., value-word1, value-word2 => ...
Guarde long na variável local. value deve ser um número inteiro longo. As variáveis locais <L> e <L> + 1 na armação de Java atual estabelecem-se em value. Há quatro destes bytecodes, um para cada um dos números inteiros 0-3: lstore_0, lstore_1, lstore_2 e lstore_3.
fstore ..., value => ...
Guarde float na variável local. value deve ser um número de ponto flutuante de precisão única. byte1 variável local na armação de Java atual estabelece-se em value.
fstore_<F> ..., value => ...
Guarde float na variável local. value deve ser um número de ponto flutuante de precisão única. <F> variável local na armação de Java atual estabelece-se em value. Há quatro destes bytecodes, um para cada um dos números inteiros 0-3: fstore_0, fstore_1, fstore_2 e fstore_3.
dstore ..., value-word1, value-word2 => ...
Guarde double na variável local. value deve ser um número de ponto flutuante de precisão dupla. As variáveis locais byte1 e byte1 + 1 na armação de Java atual estabelecem-se em value.
dstore_<D> ..., value-word1, value-word2 => ...
Guarde double na variável local. value deve ser um número de ponto flutuante de precisão dupla. As variáveis locais <D> e <D> + 1 na armação de Java atual estabelecem-se em value. Há quatro destes bytecodes, um para cada um dos números inteiros 0-3: dstore_0, dstore_1, dstore_2 e dstore_3.
astore ..., handle => ...
Referência a objeto de loja em variável local. handle deve ser um endereço de regresso ou uma referência para um objeto. byte1 variável local na armação de Java atual estabelece-se em value.
astore_<A> ..., handle => ...
Referência a objeto de loja em variável local. handle deve ser um endereço de regresso ou uma referência para um objeto. <A> variável local na armação de Java atual estabelece-se em value. Há quatro destes bytecodes, um para cada um dos números inteiros 0-3: astore_0, astore_1, astore_2 e astore_3.
iinc -no change-
Incremente a variável local pela constante. byte1 variável local na armação de Java atual deve conter um int. O seu valor incrementa-se pelo valor byte2, onde byte2 se trata como uma quantidade de 8 bits assinada.
newarray ..., size => result
Aloque a nova tabela. size deve ser um int; representa o número de elementos na nova tabela. byte1 é um código interno que indica o tipo da tabela para alocar. Os valores possíveis de byte1 são como se segue: T_BOOLEAN (4), T_chAR (5), T_FLOAT (6), T_DOUBLE (7), T_BYTE (8), T_SHORT (9), T_INT (10) e T_LONG (11).
Uma tentativa faz-se para alocar uma nova tabela do tipo indicado, capaz de manter elementos de size. Isto será o result. Se size for menos do que o zero, um NegativeArraySizeException lança-se. Se não houver bastante memória para alocar a tabela, um OutOfMemoryError lança-se. Todos os elementos da tabela inicializam-se aos seus valores à revelia.
anewarray ..., size => result
Aloque a nova tabela de objetos. size deve ser um int; representa o número de elementos na nova tabela. byte1 e byte2 usam-se para construir um índice no consórcio constante da classe atual. O item naquele índice resolve-se. A entrada resultante deve ser uma classe.
Uma tentativa faz-se para alocar uma nova tabela do tipo de classe indicado, capaz de manter elementos de size. Isto será o result. Se size for menos do que 0, um NegativeArraySizeException lança-se. Se não houver bastante memória para alocar a tabela, um OutOfMemoryError lança-se. Todos os elementos da tabela inicializam-se a null.
Observar |
anewarray usa-se para criar uma dimensão única de uma tabela de objetos. Por exemplo, o pedido novo Thread[7] gera o seguinte bytecodes: bipush 7 anewarray também pode usar-se para criar a dimensão externa de uma tabela multidimensional. Por exemplo, a declaração de tabela new int[6][] gera isto: bipush 6 (Ver a seção "Assinaturas de Método" para obter mais informações sobre cadeias como "[I".) |
multianewarray ..., size1 size2...sizeN => result
Aloque a nova tabela multidimensional. Cada size<I> deve ser um int; cada um representa o número de elementos em uma dimensão da tabela. byte1 e byte2 usam-se para construir um índice no consórcio constante da classe atual. O item naquele índice resolve-se. A entrada resultante deve ser uma classe de tabela de uma ou várias dimensões.
byte3 é um número inteiro positivo que representa o número de dimensões que se criam. Deve ser menos do que ou igual ao número de dimensões da classe de tabela. byte3 também é o número de elementos que se esticam as canelas a pilha. Todos devem ser int s maior do que ou igual ao zero. Estes usam-se como os tamanhos das dimensões. Uma tentativa faz-se para alocar uma nova tabela do tipo de classe indicado, capaz de manter elementos de size<1> * size<2> * ... * <sizeN>. Isto será o result. Se algum dos argumentos de size<I> na pilha for menos do que o zero, um NegativeArraySizeException lança-se. Se não houver bastante memória para alocar a tabela, um OutOfMemoryError lança-se.
Observar |
new int[6][3][] gera estes bytecodes: bipush 6 É mais eficiente usar newarray ou anewarray criando tabelas da dimensão única. |
arraylength ..., array => ..., length
Adquira o comprimento da tabela. array deve ser uma referência para um objeto de tabela. O length da tabela determina-se e substitui array no topo da pilha. Se array for nulo, um NullPointerException lança-se.
iaload ..., array, index => ..., value laload ..., array, index => ..., value-word1, value-word2 faload ..., array, index => ..., value daload ..., array, index => ..., value-word1, value-word2 aaload ..., array, index => ..., value baload ..., array, index => ..., value caload ..., array, index => ..., value saload ..., array, index => ..., value
Carregue <type> da tabela. array deve ser uma tabela de <type> s. index deve ser um int. O <type> value no número de posição index em array recupera-se e empurra-se para o topo da pilha. Se array for null, um NullPointerException lança-se. Se index não for dentro dos limites de array, um ArrayIndexOutOfBoundsException lança-se. <type> é, à sua vez, int, long, float, double, referência a objeto, byte, char e short. <type> s long e double tem duas palavra value s, como viu em load prévio bytecodes.
iastore ..., array, index, value => ... lastore ..., array, index, value-word1, value-word2 => ... fastore ..., array, index, value => ... dastore ..., array, index, value-word1, value-word2 => ... aastore ..., array, index, value => ... bastore ..., array, index, value => ... castore ..., array, index, value => ... sastore ..., array, index, value => ...
Loja em <type> array. array deve ser uma tabela de <type> s, index deve ser um int e value um <type>. O <type> value guarda-se na posição index em array. Se array for null, um NullPointerException lança-se. Se index não for dentro dos limites da tabela, um ArrayIndexOutOfBoundsException lança-se. <type> é, à sua vez, int, long, float, double, referência a objeto, byte, char e short. <type> s long e double tem duas palavra value s, como viu em store prévio bytecodes.
nop -no change-
Não faça nada.
pop ..., any => ...
Ponha de repente a palavra superior da pilha.
pop2 ..., any2, any1 => ...
Ponha de repente duas melhores palavras da pilha.
dup ..., any => ..., any, any
Duplique a palavra superior na pilha.
dup2 ..., any2, any1 => ..., any2, any1, any2,any1
Duplique duas melhores palavras na pilha.
dup_x1 ..., any2, any1 => ..., any1, any2,any1
Duplique a palavra superior na pilha e insira a cópia duas palavras abaixo na pilha.
dup2_x1 ..., any3, any2, any1 => ..., any2, any1, any3,any2,any1
Duplique duas melhores palavras na pilha e insira as cópias duas palavras abaixo na pilha.
dup_x2 ..., any3, any2, any1 => ..., any1, any3,any2,any1
Duplique a palavra superior na pilha e insira a cópia três palavras abaixo na pilha.
dup2_x2 ..., any4, any3, any2, any1 => ..., any2, any1, any4,any3,any2,any1
Duplique duas melhores palavras na pilha e insira as cópias três palavras abaixo na pilha.
swap ..., any2, any1 => ..., any1, any2
Troque dois melhores elementos na pilha.
iadd ..., v1, v2 => ..., result ladd ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2 fadd ..., v1, v2 => ..., result dadd ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2
v1 e v2 devem ser <type> s. O v s acrescenta-se e substitui-se na pilha pela sua soma de <type>. <type> é, à sua vez, int, long, float e double.
isub ..., v1, v2 => ..., result lsub ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2 fsub ..., v1, v2 => ..., result dsub ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2
v1 e v2 devem ser <type> s. v2 subtrai-se de v1, e ambo o v s substitui-se na pilha pela sua diferença de <type>. <type> é, à sua vez, int, long, float e double.
imul ..., v1, v2 => ..., result lmul ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2 fmul ..., v1, v2 => ..., result dmul ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2
v1 e v2 devem ser <type> s. Ambo o v s substitui-se na pilha pelo seu produto de <type>. <type> é, à sua vez, int, long, float e double.
idiv ..., v1, v2 => ..., result ldiv ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2 fdiv ..., v1, v2 => ..., result ddiv ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2
v1 e v2 devem ser <type> s. v2 divide-se por v1, e ambo o v s substitui-se na pilha pelo seu quociente de <type>. Uma tentativa de dividir-se pelo zero resulta em um ArithmeticException que se lança. <type> é, à sua vez, int, long, float e double.
irem ..., v1, v2 => ..., result lrem ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2 frem ..., v1, v2 => ..., result drem ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2
v1 e v2 devem ser <type> s. v2 divide-se por v1, e ambo o v s substitui-se na pilha pelo seu resto de <type>. Uma tentativa de dividir-se pelo zero resulta em um ArithmeticException que se lança. <type> é, à sua vez, int, long, float e double.
ineg ..., value => ..., result lneg ..., value-word1, value-word2 => ..., result-word1, result-word2 fneg ..., value => ..., result dneg ..., value-word1, value-word2 => ..., result-word1, result-word2
value deve ser um <type>. Substitui-se na pilha pela sua negação aritmética. <type> é, à sua vez, int, long, float e double.
Observar |
Agora que é familiar com o aspeto do bytecodes, os sumários que seguem ficarão mais curtos e mais curtos (por razões espaciais). Sempre pode adquirir qualquer nível desejado do detalhe da especificação de máquina virtual cheia no último lançamento de Java. |
ishl ..., v1, v2 => ..., result lshl ..., v1-word1, v1-word2, v2 => ..., r-word1, r-word2 ishr ..., v1, v2 => ..., result lshr ..., v1-word1, v1-word2, v2 => ..., r-word1, r-word2 iushr ..., v1, v2 => ..., result lushr ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2
Para tipos int e long: o turno aritmético partiu, direito de turno e direito de turno lógico.
iand ..., v1, v2 => ..., result land ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2 ior ..., v1, v2 => ..., result lor ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2 ixor ..., v1, v2 => ..., result lxor ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., r-word1, r-word2
Para tipos int e long: bitwise AND, OR e XOR.
i2l ..., value => ..., result-word1, result-word2 i2f ..., value => ..., result i2d ..., value => ..., result-word1, result-word2 l2i ..., value-word1, value-word2 => ..., result l2f ..., value-word1, value-word2 => ..., result l2d ..., value-word1, value-word2 => ..., result-word1, result-word2 f2i ..., value => ..., result f2l ..., value => ..., result-word1, result-word2 f2d ..., value => ..., result-word1, result-word2 d2i ..., value-word1, value-word2 => ..., result d2l ..., value-word1, value-word2 => ..., result-word1, result-word2 d2f ..., value-word1, value-word2 => ..., result int2byte ..., value => ..., result int2char ..., value => ..., result int2short ..., value => ..., result
Estes bytecodes convertem de um value do tipo <lhs> a um result do tipo <rhs>. <lhs> e <rhs> podem ser algum de i, l, f e d, que representam int, long, float e double, respectivamente. Três bytecodes finais convertem tipos que são evidentes.
ifeq ..., value => ... ifne ..., value => ... iflt ..., value => ... ifgt ..., value => ... ifle ..., value => ... ifge ..., value => ... if_icmpeq ..., value1, value2 => ... if_icmpne ..., value1, value2 => ... if_icmplt ..., value1, value2 => ... if_icmpgt ..., value1, value2 => ... if_icmple ..., value1, value2 => ... if_icmpge ..., value1, value2 => ... ifnull ..., value => ... ifnonnull ..., value => ...
Quando value <rel> 0 é true no primeiro jogo de bytecodes, value1 <rel> value2 é true no segundo jogo, ou value é null (ou não null) no terceiro, byte1 e byte2 usam-se para construir uma compensação de 16 bits assinada. A execução prossegue naquela compensação do pc. De outra maneira, a execução prossegue no bytecode depois. <rel> é um de eq, ne, lt, gt, le e ge, que representam igual, não igual, menos do que, maior do que, menos do que ou igual, e maior do que ou igual, respectivamente.
lcmp ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., result fcmpl ..., v1, v2 => ..., result dcmpl ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., result fcmpg ..., v1, v2 => ..., result dcmpg ..., v1-word1, v1-word2, v2-word1, v2-word2 => ..., result
v1 e v2 devem ser long, float ou double. Põem-se de repente tanto da pilha e comparam-se. Se v1 for maior do que v2, o valor de int 1 empurra-se para a pilha. Se v1 for igual a v2, 0 empurra-se para a pilha. Se v1 for menos do que v2, -1 empurra-se para a pilha. Para o ponto flutuante, se v1 ou v2 forem NaN, -1 empurra-se para a pilha do primeiro par de bytecodes, +1 do segundo par.
if_acmpeq ..., value1, value2 => ... if_acmpne ..., value1, value2 => ...
O ramo se as referências a objeto são iguais/não igual. value1 e value2 devem ser referências para objetos. Põem-se de repente ambos da pilha. Se value1 for igual/não igual a value2, byte1 e byte2 usam-se para construir uma compensação de 16 bits assinada. A execução prossegue naquela compensação do pc. De outra maneira, a execução prossegue no bytecode depois.
goto -no change- goto_w -no change-
Sempre bifurque-se. byte1 e byte2 (mais byte3 e byte4 de goto_w) usam-se para construir uma compensação de 16 bits (de 32 bits) assinada. A execução prossegue naquela compensação do pc.
jsr ... => ..., return-address jsr-w ... => ..., return-address
Subrotina de salto. O endereço do bytecode imediatamente depois do jsr empurra-se para a pilha. byte1 e byte2 (mais byte3 e byte4 de goto_w) usam-se para construir uma compensação de 16 bits (de 32 bits) assinada. A execução prossegue naquela compensação do pc.
ret -no change- ret2_w -no change-
Volte da subrotina. byte1 variável local (mais byte2 de ret_w se reúnem em um índice de 16 bits) na armação de Java atual deve conter um endereço de regresso. Os conteúdos daquela variável local escrevem-se no pc.
Observar |
jsr empurra o endereço para a pilha, e ret adquire-o fora de uma variável local. Esta assimetria é intencional. O jsr e ret bytecodes usam-se na implementação da palavra-chave de finally de Java. |
return ... => [empty]q
Volte (void) do método. Todos os valores na pilha de operand descartam-se. O intérprete então devolve o controle ao seu chamador.
ireturn ..., value => [empty] lreturn ..., value-word1, value-word2 => [empty] freturn ..., value => [empty] dreturn ..., value-word1, value-word2 => [empty] areturn ..., value => [empty]
Devolva <type> do método. value deve ser um <type>. O value empurra-se para a pilha do ambiente de execução prévio. Qualquer outro valor na pilha de operand descarta-se. O intérprete então devolve o controle ao seu chamador. <type> é, à sua vez, int, long, float, double e referência a objeto.
Observar |
O comportamento de pilha do "regresso" bytecodes pode ser confuso com cada um que espera Java operand pilha ser como a pilha de C. A pilha de operand de Java de fato compõe-se de um número de segmentos não-contíguos, cada um que corresponde a uma chamada de método. Um regresso bytecode esvazia Java operand segmento de pilha que corresponde à armação da chamada de restituição, mas não afeta o segmento de nenhuma chamada de pais. |
tableswitch ..., index => ...
tableswitch é um comprimento variável bytecode. Imediatamente depois do código de operação de tableswitch, o zero a três bytes de 0 insere-se como enchimento para que o seguinte byte comece em um endereço que é um múltiplo de quatro. Depois do enchimento são uma série de quantidades de 4 bytes assinadas: default-offset, low, high, e logo (high - low + 1) além disso assinou compensações de 4 bytes. Estas compensações tratam-se como um 0 - mesa de salto baseada.
O index deve ser um int. Se index for menos do que low ou index são maiores do que high, default-offset acrescenta-se ao pc. De outra maneira, o (index - low) th o elemento da mesa de salto extrai-se e acrescenta-se ao pc.
lookupswitch ..., key => ...
lookupswitch é um comprimento variável bytecode. Imediatamente depois do código de operação de lookupswitch, o zero a três bytes de 0 insere-se como enchimento para que o seguinte byte comece em um endereço que é um múltiplo de quatro. Imediatamente depois que o enchimento é uma série de pares de quantidades de 4 bytes assinadas. O primeiro par é especial; contém o default-offset e o número de pares que seguem. Cada par subsequente compõe-se de um match e um offset.
O key na pilha deve ser um int. Este key é em comparação com cada um do match es. Se for igual a um deles, o offset correspondente acrescenta-se ao pc. Se o key não combinar com nenhum do match es, o default-offset acrescenta-se ao pc.
putfield ..., handle, value => ... putfield ..., handle, value-word1, value-word2 => ...
Campo de jogo em objeto. byte1 e byte2 usam-se para construir um índice no consórcio constante da classe atual. O item de consórcio constante é uma referência de campanha para um nome de classe e um nome de campo. O item resolve-se a um ponteiro de bloco de campanha que contém a largura do campo e compensa-se (ambos em bytes).
O campo naquela compensação da partida do exemplo apontado para por handle vai se estabelecer no value no topo da pilha. O primeiro quadro de pilha é para o de 32 bits, e o segundo de campos de 64 bits de largura. Este bytecode trata ambos. Se handle for null, um NullPointerException lança-se. Se o campo especificado for um campo de static, um IncompatibleClassChangeError lança-se.
getfield ..., handle => ..., value getfield ..., handle => ..., value-word1, value-word2
Traga o campo do objeto. byte1 e byte2 usam-se para construir um índice no consórcio constante da classe atual. O item de consórcio constante será uma referência de campanha para um nome de classe e um nome de campo. O item resolve-se a um ponteiro de bloco de campanha que contém a largura do campo e compensa-se (ambos em bytes).
handle deve ser uma referência para um objeto. O value na compensação no objeto referido por handle substitui handle no topo da pilha. O primeiro quadro de pilha é para o de 32 bits, e o segundo de campos de 64 bits de largura. Este bytecode trata ambos. Se o campo especificado for um campo de static, um IncompatibleClassChangeError lança-se.
putstatic ..., value => ... putstatic ..., value-word1, value-word2 => ...
Estabeleça o campo estático na classe. byte1 e byte2 usam-se para construir um índice no consórcio constante da classe atual. O item de consórcio constante será uma referência de campanha para um campo estático de uma classe. Aquele campo vai se fazer para ter o value no topo da pilha. O primeiro quadro de pilha é para o de 32 bits, e o segundo de campos de 64 bits de largura. Este bytecode trata ambos. Se o campo especificado não for um campo estático, um IncompatibleClassChangeError lança-se.
getstatic ..., => ..., value_ getstatic ..., => ..., value-word1, value-word2
Adquira o campo estático da classe. byte1 e byte2 usam-se para construir um índice no consórcio constante da classe atual. O item de consórcio constante será uma referência de campanha para um campo estático de uma classe. O value daquele campo coloca-se no topo da pilha. O primeiro quadro de pilha é para o de 32 bits, e o segundo de campos de 64 bits de largura. Este bytecode trata ambos. Se o campo especificado não for um campo estático, um IncompatibleClassChangeError lança-se.
invokevirtual ..., handle, [arg1, [arg2, ...]], ... => ...
Invoque o método de exemplo baseado no tipo em tempo de execução. A pilha de operand deve conter uma referência para um objeto e algum número de argumentos. byte1 e byte2 usam-se para construir um índice no consórcio constante da classe atual. O item naquele índice no consórcio constante contém a assinatura de método completa. Um ponteiro para a mesa de método do objeto recupera-se da referência a objeto. A assinatura de método procura-se na mesa de método. A assinatura de método garante-se para combinar exatamente com uma das assinaturas de método na mesa.
O resultado da busca é um índice na mesa de método da classe denominada isto usa-se para olhar na mesa de método do tipo em tempo de execução do objeto, onde um ponteiro para o bloco de método do método combinado se encontra. O bloco de método indica o tipo do método (native, synchronized, e assim por diante) e o número de argumentos (nargs) esperado na pilha de operand.
Se o método for synchronized marcado, o monitor associado com handle introduz-se.
A base da tabela de variáveis local da nova armação de pilha de Java faz-se para apontar para handle na pilha, fazendo handle e os argumentos fornecidos (arg1, arg2, e assim por diante) o primeiro nargs as variáveis locais da nova armação. O número total de variáveis locais usadas pelo método determina-se, e o ambiente de execução da nova armação empurra-se depois de deixar quarto suficiente dos habitantes locais. A base da pilha de operand desta invocação de método estabelece-se na primeira palavra depois do ambiente de execução. Finalmente, a execução continua com o primeiro bytecode do método combinado.
Se handle for null, um NullPointerException lança-se. Se durante a invocação de método um excesso de pilha se descobrir, um StackOverflowError lança-se.
invokenonvirtual ..., handle, [arg1, [arg2, ...]], ... => ...
Invoque o método de exemplo baseado em compilam o tipo vezes. A pilha de operand deve conter uma referência (handle) a um objeto e algum número de argumentos. byte1 e byte2 usam-se para construir um índice no consórcio constante da classe atual. O item naquele índice no consórcio constante contém a assinatura de método completa e classe. A assinatura de método procura-se na mesa de método da classe indicada. A assinatura de método garante-se para combinar exatamente com uma das assinaturas de método na mesa.
O resultado da busca é um bloco de método. O bloco de método indica o tipo do método (native, synchronized, e assim por diante) e o número de argumentos (nargs) esperado na pilha de operand. (Três parágrafos últimos são idênticos ao bytecode prévio.)
invokestatic ..., , [arg1, [arg2, ...]], ... => ...
Invoque a classe (static) método. A pilha de operand deve conter algum número de argumentos. byte1 e byte2 usam-se para construir um índice no consórcio constante da classe atual. O item naquele índice no consórcio constante contém a assinatura de método completa e classe. A assinatura de método procura-se na mesa de método da classe indicada. A assinatura de método garante-se para combinar com uma das assinaturas de método na mesa de método da classe exatamente.
O resultado da busca é um bloco de método. O bloco de método indica o tipo do método (native, synchronized, e assim por diante) e o número de argumentos (nargs) esperado na pilha de operand.
Se o método for synchronized marcado, o monitor associado com a classe introduz-se. (Dois parágrafos últimos são idênticos àqueles em invokevirtual, exceto que nenhum NullPointerException pode lançar-se.)
invokeinterface ..., handle, [arg1, [arg2, ...]], ...=> ...
Invoque o método de interface. A pilha de operand deve conter uma referência (handle) a um objeto e algum número de argumentos. byte1 e byte2 usam-se para construir um índice no consórcio constante da classe atual. O item naquele índice no consórcio constante contém a assinatura de método completa. Um ponteiro para a mesa de método do objeto recupera-se da referência a objeto. A assinatura de método procura-se na mesa de método. A assinatura de método garante-se para combinar exatamente com uma das assinaturas de método na mesa.
O resultado da busca é um bloco de método. O bloco de método indica o tipo do método (native, synchronized, e assim por diante) mas, diferentemente do outro "invocar" bytecodes, o número de argumentos disponíveis (nargs) se toma de byte3; byte4 reserva-se para o futuro uso. (Três parágrafos últimos são idênticos àqueles em invokevirtual.)
Sathrow ..., handle => [undefined]
Exceção de lançamento. handle deve ser uma maçaneta a um objeto de exceção. Aquela exceção, que deve ser um exemplo de Throwable (ou uma subclasse), lança-se. A armação de pilha de Java atual procura-se a cláusula catch mais recente que trata a exceção. Se uma "entrada" de lista de proveito que combina se encontrar, o pc reinicializa-se com o endereço indicado pelo ponteiro de lista do proveito, e a execução continua lá.
Se nenhuma cláusula catch apropriada se encontrar na armação de pilha atual, aquela armação põe-se de repente e a exceção relança-se, começando o processo mais uma vez na armação de pais. Se handle for null, um NullPointerException lança-se em vez disso.
new ... => ..., handle
Crie o novo objeto. byte1 e byte2 usam-se para construir um índice no consórcio constante da classe atual. O item naquele índice deve ser um nome de classe que pode resolver-se a um ponteiro de classe. Um novo exemplo daquela classe então cria-se e uma referência (handle) do exemplo coloca-se no topo da pilha.
checkcast ..., handle => ..., [handle | ...]
Assegure-se que o objeto é do tipo dado. handle deve ser uma referência para um objeto. byte1 e byte2 usam-se para construir um índice no consórcio constante da classe atual. Supõe-se que a cadeia naquele índice do consórcio constante é um nome de classe que pode resolver-se a um ponteiro de classe.
checkcast determina se handle pode lançar-se a uma referência para um objeto daquela classe. (Um null handle pode lançar-se a qualquer classe.) Se handle puder lançar-se legalmente, o produto de execução no seguinte bytecode e o handle permanecem na pilha. Se não, um ClassCastException se lançar e a pilha esvazia-se.
instanceof ..., handle => ..., result
Determine se o objeto é do tipo dado. handle deve ser uma referência para um objeto. byte1 e byte2 usam-se para construir um índice no consórcio constante da classe atual. Supõe-se que a cadeia naquele índice do consórcio constante é um nome de classe que pode resolver-se a um ponteiro de classe.
Se handle for null, o result é 0 (false). De outra maneira, instanceof determina se handle pode lançar-se a uma referência para um objeto daquela classe. O result é 1 (true) se puder, e 0 (false) de outra maneira.
monitorenter ..., handle => ...
Entre na região controlada do código. handle deve ser uma referência para um objeto. O intérprete tenta obter o acesso exclusivo via um mecanismo de fechadura a handle. Se outro fio já mandar trancar handle, o fluxo corrente espera até que o handle se destranque. Se o fluxo corrente já mandar trancar handle, a execução continua normalmente. Se handle não tiver fechadura nele, este bytecode obtém uma fechadura exclusiva. (Um null em qualquer bytecode lança NullPointerException.)
Smonitorexit ..., handle => ...
A saída controlou a região do código. handle deve ser uma referência para um objeto. A fechadura em handle lança-se. Se isto for a fechadura última que este fio tem em que se permite que handle (se permite que um fio tenha múltiplas fechaduras em um handle único), outros fios que esperam por handle prossiga. (Um null em qualquer bytecode lança NullPointerException.)
breakpoint -no change-
Chame o treinador de limite. O limite bytecode usa-se para sobregravar de um bytecode para forçar o controle temporariamente atrás para o depurador antes do efeito do bytecode sobregravado. Os operands do bytecode original (se algum) não se sobregravam, e o bytecode original restaura-se quando o limite bytecode se retira.
A seguinte discussão, diretamente fora de Java documentação de máquina virtual, mostra-lhe um exemplo da inteligência mencionou antes que isto é necessário para fazer um intérprete bytecode rápido:
O seguinte jogo de pseudo-bytecodes, suffixed por _quick, é todas as variantes de Java padrão bytecodes. Usam-se pelo tempo de execução para melhorar a velocidade de execução do intérprete bytecode. Não são oficialmente parte da especificação de máquina virtual e são invisíveis do lado de fora de Java a implementação de máquina virtual. Contudo, dentro daquela implementação provaram ser uma otimização eficaz.
Em primeiro lugar, deve saber que o javac compilador de Java ainda só gera non-_quick bytecodes. Em segundo lugar, todos os bytecodes que têm uma variante de _quick referem o consórcio constante. A otimização de When _quick acende-se, cada non-_quick bytecode (que tem uma variante de _quick) resolve o item especificado no consórcio constante, transmite um erro se o item no consórcio constante não pode resolver-se por alguma razão, se converter na variante de _quick de si mesmo, e logo executar a sua operação desejada.
Isto é idêntico às ações do non-_quick bytecode, exceto o passo da sobregravação de si mesmo com a sua variante de _quick. A variante de _quick de um bytecode supõe que o item no consórcio constante já se tenha resolvido, e que esta resolução não produziu nenhum erro. Simplesmente executa a operação desejada no item resolvido.
Assim, como os seus bytecodes estão interpretando-se, tornam-se automaticamente mais rápidos e mais rápidos! Aqui estão todas as variantes de _quick no tempo de execução de Java atual:
ldc1_quick ldc2_quick ldc2w_quick anewarray_quick multinewarray_quick putfield_quick putfield2_quick getfield_quick getfield2_quick putstatic_quick putstatic2_quick getstatic_quick getstatic2_quick invokevirtual_quick invokevirtualobject_quick invokenonvirtual_quick invokestatic_quick invokeinterface_quick new_quick checkcast_quick instanceof_quick
Se você gostaria de voltar neste apêndice e olhada para o que cada um destes faz, pode encontrar o nome do bytecode original no qual uma variante de _quick se baseia simplesmente retirando o _quick do seu nome. O putstatic bytecodes, getstatic, putfield e getfield têm duas variantes de _quick cada um, um para cada quadro de pilha nas suas descrições originais. invokevirtual tem duas variantes: um para objetos e um para tabelas (para fazer buscas rápidas em java.lang.Object).
Observar |
Uma nota última da otimização de _quick, quanto ao manejo excepcional do consórcio constante (para admiradores de detalhe só): Quando uma classe se lê em, uma tabela constant_pool[] do tamanho nconstants cria-se e destina-se a um campo na classe. constant_pool[0] faz-se para apontar para uma tabela dinamicamente alocada que indica que campos no constant_pool já se resolveram. Constant_pool[1] por constant_pool[nconstants - 1] faz-se para apontar para o campo "de tipo" que corresponde a este item constante. Quando um bytecode se realiza que referem o consórcio constante, um índice gera-se, e constant_pool[0] verifica-se para ver se o índice já se resolveu. Nesse caso o valor de constant_pool[index] devolve-se. Se não, o valor de constant_pool[index] se resolve para ser o ponteiro real ou dados, e sobregrava de qualquer valor já esteve em constant_pool[index]. |