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. long
e os double
tipos, 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 ( impdep1
e 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, iadd
irá adicionar dois inteiros, enquanto dadd
irá adicionar dois duplos. A const
, load
, e store
as 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.
_n
load
store
const
As const
instruções colocam um valor do tipo especificado na pilha. Por exemplo, iconst_5
irá inserir um número inteiro (valor de 32 bits) com o valor 5 na pilha, enquanto dconst_1
irá 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 null
referência. O n para as instruções load
e store
especifica o índice na matriz de variável local para carregar ou armazenar. A aload_0
instrução coloca o objeto na variável local 0 na pilha (geralmente é o this
objeto). istore_1
armazena 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:
- Fusão a frio
- JRuby e Jython , duas linguagens de script baseadas em Ruby e Python
- Apache Groovy , linguagem opcionalmente tipada e dinâmica de uso geral, com tipagem estática e recursos de compilação estática
- Scala , uma linguagem de programação de uso geral segura para tipos que oferece suporte à programação orientada a objetos e funcional
- JGNAT e AppletMagic, compilam da linguagem Ada para bytecode Java
- Compiladores de código de bytes C para Java
- Clojure , uma linguagem de programação funcional, imutável e de propósito geral na família Lisp com forte ênfase na simultaneidade
- Kawa , uma implementação da linguagem de programação Scheme , também um dialeto do Lisp .
- MIDletPascal
- O código JavaFX Script é compilado para bytecode Java
- Kotlin , uma linguagem de programação de uso geral com tipagem estática com inferência de tipo
- O código-fonte Object Pascal é compilado em bytecode Java usando o compilador Free Pascal 3.0+.
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 invokedynamic
instruçã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 invokevirtual
instruçã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 invokedynamic
opcode.
Veja também
- Listagens de instruções de bytecode Java
- Arquivo de classe Java
- Lista de linguagens JVM
- Ferramentas de backporting Java
- Compiladores de C para Java Virtual Machine
- JStik
- Common Intermediate Language (CIL), rival da Microsoft para o bytecode Java
- ObjectWeb ASM
- Biblioteca de Engenharia de Código de Byte
Referências
links externos
- Especificação de Java Virtual Machine da Oracle
- Linguagens de programação para a máquina virtual Java
- Bytecode Visualizer - visualizador de bytecode e depurador (plugin gratuito do Eclipse)
- AdaptJ StackTrace - depuração em nível de bytecode com controle total da pilha, das variáveis locais e do fluxo de execução
- Java Class Unpacker - plugin para Total Commander, permite abrir arquivos de classe como arquivos compactados e ver campos e métodos como arquivos. O bytecode pode ser visto como texto usando F3