Bytecode Java - Java bytecode

Bytecode Java é o conjunto de instruções da máquina virtual Java (JVM).

Relação com Java

Um programador Java não precisa conhecer ou compreender o bytecode Java. No entanto, conforme sugerido no jornal IBM developerWorks, "Entender o bytecode e qual bytecode provavelmente será gerado por um compilador Java ajuda o programador Java da mesma forma que o conhecimento de montagem ajuda o programador C ou C ++ ."

Arquitetura do conjunto de instruções

A JVM é uma máquina de pilha e uma máquina de registro . Cada quadro para uma chamada de método possui uma "pilha de operandos" e uma matriz de "variáveis ​​locais". A pilha de operandos é usada para operandos para cálculos e para receber o valor de retorno de um método chamado, enquanto variáveis ​​locais têm o mesmo propósito que registradores e também são usadas para passar argumentos de método. O tamanho máximo da pilha de operandos e do array de variáveis ​​locais, calculado pelo compilador, faz parte dos atributos de cada método. Cada um pode ser dimensionado independentemente de 0 a 65535 valores, onde cada valor é de 32 bits. longe os doubletipos, que são de 64 bits, ocupam duas variáveis ​​locais consecutivas (que não precisam ser alinhadas a 64 bits na matriz de variáveis ​​locais) ou um valor na pilha de operandos (mas são contados como duas unidades na profundidade da pilha) .

Conjunto de instruções

Cada bytecode é composto por um byte que representa o opcode , junto com zero ou mais bytes para operandos.

Dos 256 códigos de operação de bytes longos possíveis , a partir de 2015, 202 estão em uso (~ 79%), 51 são reservados para uso futuro (~ 20%) e 3 instruções (~ 1%) são reservadas permanentemente para implementações JVM para usar. Dois deles ( impdep1e impdep2) são para fornecer armadilhas para software e hardware específicos de implementação, respectivamente. O terceiro é usado para depuradores para implementar pontos de interrupção.

As instruções se enquadram em vários grupos amplos:

  • Carregar e armazenar (por exemplo aload_0, istore)
  • Aritmética e lógica (por exemplo ladd, fcmpl)
  • Conversão de tipo (por exemplo i2b, d2i)
  • Criação e manipulação de objetos ( new, putfield)
  • Gerenciamento de pilha de operandos (por exemplo swap, dup2)
  • Transferência de controle (por exemplo ifeq, goto)
  • Chamada e retorno de método (por exemplo invokespecial, areturn)

Existem também algumas instruções para uma série de tarefas mais especializadas, como lançamento de exceções, sincronização, etc.

Muitas instruções possuem prefixos e / ou sufixos referentes aos tipos de operandos sobre os quais operam. São os seguintes:

Prefixo sufixo Tipo de operando
i inteiro
l grande
s baixo
b byte
c personagem
f flutuador
d Duplo
a referência

Por exemplo, iaddirá adicionar dois inteiros, enquanto daddirá adicionar dois duplos. A const, load, e storeas instruções podem também levar um sufixo da forma , em que n é um número de 0-3 por e . O n máximo para difere por tipo. _nloadstoreconst

As constinstruções colocam um valor do tipo especificado na pilha. Por exemplo, iconst_5irá inserir um número inteiro (valor de 32 bits) com o valor 5 na pilha, enquanto dconst_1irá inserir um duplo (valor de ponto flutuante de 64 bits) com o valor 1 na pilha. Há também um aconst_null, que empurra uma nullreferência. O n para as instruções loade storeespecifica o índice na matriz de variável local para carregar ou armazenar. A aload_0instrução coloca o objeto na variável local 0 na pilha (geralmente é o thisobjeto). istore_1armazena o inteiro no topo da pilha na variável local 1. Para variáveis ​​locais além de 3, o sufixo é descartado e operandos devem ser usados.

Exemplo

Considere o seguinte código Java:

outer:
for (int i = 2; i < 1000; i++) {
    for (int j = 2; j < i; j++) {
        if (i % j == 0)
            continue outer;
    }
    System.out.println (i);
}

Um compilador Java pode traduzir o código Java acima em bytecode da seguinte maneira, assumindo que o acima foi colocado em um método:

0:   iconst_2
1:   istore_1
2:   iload_1
3:   sipush  1000
6:   if_icmpge       44
9:   iconst_2
10:  istore_2
11:  iload_2
12:  iload_1
13:  if_icmpge       31
16:  iload_1
17:  iload_2
18:  irem
19:  ifne    25
22:  goto    38
25:  iinc    2, 1
28:  goto    11
31:  getstatic       #84; // Field java/lang/System.out:Ljava/io/PrintStream;
34:  iload_1
35:  invokevirtual   #85; // Method java/io/PrintStream.println:(I)V
38:  iinc    1, 1
41:  goto    2
44:  return

Geração

A linguagem mais comum voltada para a máquina virtual Java produzindo bytecode Java é Java. Originalmente, existia apenas um compilador, o compilador javac da Sun Microsystems , que compila o código-fonte Java para bytecode Java; mas como todas as especificações para bytecode Java estão agora disponíveis, outras partes forneceram compiladores que produzem bytecode Java. Exemplos de outros compiladores incluem:

  • Compilador Eclipse para Java (ECJ)
  • Jikes , compila de Java para bytecode Java (desenvolvido pela IBM , implementado em C ++ )
  • Espresso, compila de Java para bytecode Java (apenas Java 1.0)
  • GNU Compiler for Java (GCJ), compila de Java para bytecode Java; ele também pode compilar para código de máquina nativo e fazia parte da GNU Compiler Collection (GCC) até a versão 6.

Alguns projetos fornecem montadores Java para permitir a escrita de bytecode Java manualmente. O código de montagem também pode ser gerado por máquina, por exemplo, por um compilador visando uma máquina virtual Java . Montadores Java notáveis ​​incluem:

  • Jasmin leva descrições de texto para classes Java, escritas em uma sintaxe simples semelhante a um assembly usando o conjunto de instruções da máquina virtual Java e gera um arquivo de classe Java
  • Jamaica, uma linguagem de macro assembly para a máquina virtual Java . A sintaxe Java é usada para definição de classe ou interface. Os corpos dos métodos são especificados usando instruções de bytecode.
  • Krakatau Bytecode Tools, atualmente contém três ferramentas: um descompilador e desmontador para classfiles Java e um assembler para criar classfiles.
  • Lilac, um montador e desmontador para a máquina virtual Java .

Outros desenvolveram compiladores, para diferentes linguagens de programação, para atingir a máquina virtual Java, como:

Execução

Existem várias máquinas virtuais Java disponíveis hoje para executar bytecode Java, tanto produtos gratuitos quanto comerciais. Se a execução de bytecode em uma máquina virtual for indesejável, um desenvolvedor também pode compilar o código-fonte Java ou bytecode diretamente para o código de máquina nativo com ferramentas como o GNU Compiler for Java (GCJ). Alguns processadores podem executar bytecode Java nativamente. Esses processadores são chamados de processadores Java .

Suporte para linguagens dinâmicas

A máquina virtual Java fornece algum suporte para linguagens digitadas dinamicamente . A maior parte do conjunto de instruções JVM existente é tipado estaticamente - no sentido de que as chamadas de método têm seu tipo de assinatura verificado em tempo de compilação , sem um mecanismo para adiar essa decisão em tempo de execução ou para escolher o despacho de método por uma abordagem alternativa.

JSR 292 ( Suportando Linguagens de Tipagem Dinâmica na Plataforma Java ) adicionou uma nova invokedynamicinstrução no nível de JVM, para permitir a invocação de método baseada na verificação de tipo dinâmico (em vez da invokevirtualinstrução existente com verificação de tipo estática ). A Máquina Da Vinci é um protótipo de implementação de máquina virtual que hospeda extensões JVM destinadas a oferecer suporte a linguagens dinâmicas. Todas as JVMs que suportam JSE 7 também incluem o invokedynamicopcode.

Veja também

Referências

links externos