Todo desenvolvedor Java logo aprende que o código-fonte Java é compilado e gera bytecodes que formam o executável Java. Mas o que são exatamente esses bytecodes e o que fazem já é um ponto que muitos desenvolvedores ignoram.
Quando trabalhamos com uma tecnologia é sempre útil entender melhor sua base, seus fundamentos. Com o Java, conhecer um pouco mais dos bytecodes pode ajudar em alguns momentos.
Por isso, segue abaixo uma explicação sobre o bytecode Java e exemplo comentado.
Bytecodes da Máquina Virtual Java
Ao compilar nosso código-fonte Java, é gerado um arquivo com extensão .class contendo diversos bytecodes. Assim como o código compilado da linguagem C, por exemplo, gera código de máquina, esses bytecodes do código compilado Java também são linguagem de máquina.
A grande diferença aqui é que quando desenvolvemos em C, devemos compilar nosso código-fonte com um compilador específico para cada sistema operacional, que irá gerar código específico para aquela máquina. No mundo Java, por sua vez, temos a necessidade de compilar apenas uma vez, gerando código para uma máquina virtual Java (JVM). Daí vem o mantra Java “code once, run everywhere” (codifique uma vez, rode em qualquer lugar), que ilustra a característica Java de ser cross-platform.
Essa idéia da JVM torna o Java uma plataforma extremamente poderosa. Se for possível criar uma JVM para algum tipo de máquina, será possível rodar, na teoria, qualquer código Java que você tenha escrito. Com isso, é possível termos o Java rodando em diversos sistemas operacionais, dispositivos móveis, etc.
A JVM nada mais é do que uma ponte, que executa as instruções de máquina genéricas (bytecodes) compiladas a partir do código-fonte Java, análogas aos mnemônicos do assembly, e as traduz para as instruções específicas do sistema operacional e hardaware utilizados.
O código compilado Java é formado por instruções conhecidas como opcodes com o tamanho de 1 byte, vindo daí o nome bytecodes.
Exemplo
Código-fonte Java
Abaixo temos um código simples Java, um laço de repetição:
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); }
Bytecodes
Ao compilarmos o código acima, os bytecodes gerados seriam mais ou menos como os abaixo:
0: iconst_2 # carrega valor literal '2' na pilha 1: istore_1 # armazena valor carregado anteriormente na variável 1 (variável i) 2: iload_1 # carrega a variável 1 (variável i) na pilha 3: sipush 1000 # carrega valor literal '1000' (short) na pilha 6: if_icmpge 44 # se primeiro valor carregado (variável i) for maior ou igual ao segundo valor carregado (literal '1000'), vai para linha 44 (return) 9: iconst_2 # carrega valor literal '2' na pilha 10: istore_2 # armazena valor carregado anteriormente na variável 2 (j) 11: iload_2 # carreja variável 2 (variável j) na pilha 12: iload_1 # carrega variável 1 (variável i) na pilha 13: if_icmpge 31 # se primeiro valor carregado (variável j) for maior ou igual ao segundo valor carregado (variável i), vai para linha 32 (fora do loop) 16: iload_1 # carrega variável 1 (variável i) na pilha 17: iload_2 # carrega variável 2 (variável j) na pilha 18: irem # faz módulo (resto) dos dois valores carregados na memória (i % j) 19: ifne 25 # se resultado anterior não for zero, vai para linha 25 (loop interno) 22: goto 38 # se não saiu do loop (módulo é zero), vai para linha 38 (label outer) 25: iinc 2, 1 # incrementa variável da segunda posição da memória (variável j) de 1 28: goto 11 # vai para linha 11 (loop interno) 31: getstatic #84; //Field java/lang/System.out:Ljava/io/PrintStream; # carrega um valor estático de uma classe (variável System.out) 34: iload_1 # carrega variável 1 (variável i) na pilha 35: invokevirtual #85; //Method java/io/PrintStream.println:(I)V, # invoca método println com parametro sendo o valor carregado na pilha (variável i) 38: iinc 1, 1 # incrementa variável da primeira posição da memória (variável i) de 1 41: goto 2 # vai para linha 2 (loop externo) 44: return # retorno void do método
Referências
Livro “Introdução à arquitetura e design de software: uma visão sobre a plataforma Java” – Paulo Silveira, Guilherme Silveira, Sérgio Lopes, Guilherme Moreira, Nico Steppat, Fabio Kung
Artigo “Java Bytecode” – Wikipedia