Olá pessoal! Uma tradução do artigo foi preparada especificamente para os alunos do curso Java Developer .
Continuamos a falar sobre como a Java Virtual Machine funciona internamente. No
artigo anterior (
original em inglês ), examinamos o subsistema de carregamento de classes. Neste artigo, falaremos sobre a estrutura dos arquivos de classe.
Como já sabemos, todo o código-fonte escrito na linguagem de programação Java é compilado primeiro no
bytecode usando o compilador
javac
, que faz parte do Java Development Kit. O bytecode é armazenado em um arquivo binário em um arquivo de classe especial. Então esses arquivos de classe são dinamicamente (se necessário) carregados na memória pelo carregador de classes (ClassLoader).
Figura - Compilação do código-fonte JavaCada arquivo com uma
.java
compilado em pelo menos um arquivo
.class
. Para cada classe, interface e módulo definido no código-fonte, um arquivo
.class
é criado. Isso também se aplica a interfaces e classes aninhadas.
Nota - para simplificar, os arquivos com a extensão
.class
serão chamados de "arquivos de classe".
Vamos escrever um programa simples.
public class ClassOne{ public static void main(String[] args){ System.out.println("Hello world"); } static class StaticNestedClass{ } } class ClassTwo{ } interface InterfaceOne{ }
A execução do
javac
para este arquivo resultará nos seguintes arquivos.
ClassOne$StaticNestedClass.class ClassOne.class ClassTwo.class InterfaceOne.class
Como você pode ver, um arquivo de classe separado é criado para cada classe e interface.
O que há dentro do arquivo de classe?
O arquivo de classe está no formato binário. As informações são geralmente escritas sem recuo entre informações consecutivas, tudo está alinhado com os limites de bytes. Todos os valores de 16 e 32 bits são gravados usando dois ou quatro bytes consecutivos de 8 bits.
O arquivo de classe contém as seguintes informações.
Número mágico, assinatura . Os primeiros quatro bytes de cada arquivo de classe são sempre
0xCAFEBABE
. Esses quatro bytes identificam o arquivo de classe Java.
Versão do arquivo. Os próximos quatro bytes contêm as versões principais e secundárias do arquivo. Juntos, esses números determinam a versão do formato do arquivo de classe. Se o arquivo de classe tiver uma versão principal principal de M e um m menor, designamos essa versão como Mm
Cada JVM possui limitações nas versões suportadas dos arquivos de classe. Por exemplo, o Java 11 suporta versões principais de 45 a 55, Java 12 - de 45 a 56.
Poça de constantes. Uma tabela de estruturas representando constantes de seqüência de caracteres, nomes de classes, interfaces, campos, métodos e outras constantes que estão na estrutura ClassFile e suas subestruturas. Cada elemento do conjunto constante começa com uma tag de byte único que define o tipo de constante. Dependendo do tipo de constante, os seguintes bytes podem ser um valor constante imediato ou uma referência a outro elemento no pool.
Sinalizadores de acesso. Uma lista de sinalizadores que indicam a classe é uma interface, pública ou privada, a classe final ou não. Vários sinalizadores como
ACC_PUBLIC
,
ACC_FINAL
,
ACC_INTERFACE
,
ACC_ENUM
etc. são descritos na Especificação da Java Virtual Machine.
Essa aula. Link para a entrada no pool constante.
Super classe. Link para a entrada no pool constante.
Interfaces O número de interfaces implementadas pela classe.
O número de campos. O número de campos na classe ou interface.
Campos. Após o número de campos, é apresentada uma tabela de estruturas de comprimento variável. Um para cada campo com uma descrição do tipo e nome do campo (com referência ao conjunto de constantes).
Número de métodos. O número de métodos na classe ou interface. Esse número inclui apenas métodos explicitamente definidos na classe, sem métodos herdados das superclasses.
Métodos A seguir, os próprios métodos. Para cada método, estão contidas as seguintes informações: o descritor do método (tipo de retorno e lista de argumentos), o número de palavras necessárias para as variáveis locais do método, o número máximo de palavras de pilha necessárias para a pilha de operandos do método, a tabela de exceção capturada pelo método, bytecodes do método e a tabela números de linha.
O número de atributos. O número de atributos nesta classe, interface ou módulo.
Atributos O número de atributos é seguido por tabelas ou estruturas de comprimento variável que descrevem cada atributo. Por exemplo, sempre há um atributo "SourceFile". Ele contém o nome do arquivo de origem do qual o arquivo de classe foi compilado.
Embora o arquivo de classe não seja diretamente legível por humanos, existe uma ferramenta no JDK chamada
javap que exibe seu conteúdo em um formato conveniente.
Vamos escrever um programa Java simples, como mostrado abaixo.
package bytecode; import java.io.Serializable; public class HelloWorld implements Serializable, Cloneable { public static void main(String[] args) { System.out.println("Hello World"); } }
Vamos compilar este programa com o
javac
, que criará o arquivo
HelloWorld.class
e use o
javap
para visualizar o arquivo
HelloWorld.class
. A execução do
javap
com a opção
-v (verbose)
para
HelloWorld.class
fornece o seguinte resultado:
Classfile /Users/apersiankite/Documents/code_practice/java_practice/target/classes/bytecode/HelloWorld.class Last modified 02-Jul-2019; size 606 bytes MD5 checksum 6442d93b955c2e249619a1bade6d5b98 Compiled from "HelloWorld.java" public class bytecode.HelloWorld implements java.io.Serializable,java.lang.Cloneable minor version: 0 major version: 55 flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #5 // bytecode/HelloWorld super_class: #6 // java/lang/Object interfaces: 2, fields: 0, methods: 2, attributes: 1 Constant pool: #1 = Methodref #6.#22
Aqui você pode ver que a classe é pública e possui 37 entradas no pool constante. Há um atributo (SourceFile abaixo), a classe implementa duas interfaces (Serializable, Cloneable), não possui campos e existem dois métodos.
Você deve ter notado que existe apenas um método principal estático no código-fonte, mas o arquivo de classe diz que existem dois métodos. Lembre-se do construtor padrão - este é um construtor sem argumento adicionado pelo compilador
javac
, cujo bytecode também é visível na saída. Construtores são considerados como métodos.
Você pode ler mais sobre o javap
aqui .
Dica : você também pode usar o javap para ver como as lambdas diferem das classes internas anônimas.