Hola a todos! Se preparó una traducción del artículo específicamente para estudiantes del curso Java Developer .
Continuamos hablando sobre cómo funciona la máquina virtual Java internamente. En el
artículo anterior (
original en inglés ), examinamos el subsistema de carga de clases. En este artículo hablaremos sobre la estructura de los archivos de clase.
Como ya sabemos, todo el código fuente escrito en el lenguaje de programación Java se compila primero en
bytecode utilizando el compilador
javac
, que forma parte del Kit de desarrollo de Java. El bytecode se almacena en un archivo binario en un archivo de clase especial. Luego, estos archivos de clase se cargan dinámicamente (si es necesario) en la memoria mediante el cargador de clases (ClassLoader).
Figura - Compilación de código fuente JavaCada archivo con una
.java
compila en al menos un archivo
.class
. Para cada clase, interfaz y módulo definidos en el código fuente, se crea un archivo
.class
. Esto también se aplica a las interfaces y clases anidadas.
Nota: para simplificar, los archivos con la extensión
.class
se denominarán "archivos de clase".
Escribamos un programa simple.
public class ClassOne{ public static void main(String[] args){ System.out.println("Hello world"); } static class StaticNestedClass{ } } class ClassTwo{ } interface InterfaceOne{ }
Ejecutar
javac
para este archivo dará como resultado los siguientes archivos.
ClassOne$StaticNestedClass.class ClassOne.class ClassTwo.class InterfaceOne.class
Como puede ver, se crea un archivo de clase separado para cada clase e interfaz.
¿Qué hay dentro del archivo de clase?
El archivo de clase está en formato binario. La información que contiene generalmente se escribe sin sangría entre piezas consecutivas de información, todo está alineado con los límites de bytes. Todos los valores de 16 bits y 32 bits se escriben utilizando dos o cuatro bytes consecutivos de 8 bits.
El archivo de clase contiene la siguiente información.
Número mágico, firma . Los primeros cuatro bytes de cada archivo de clase son siempre
0xCAFEBABE
. Estos cuatro bytes identifican el archivo de clase Java.
Versión de archivo Los siguientes cuatro bytes contienen las versiones principales y secundarias del archivo. Juntos, estos números determinan la versión del formato de archivo de clase. Si el archivo de clase tiene una versión principal mayor de M y una menor m, entonces designamos esta versión como Mm
Cada JVM tiene limitaciones en las versiones compatibles de los archivos de clase. Por ejemplo, Java 11 admite versiones principales de 45 a 55, Java 12, de 45 a 56.
Pool de constantes. Una tabla de estructuras que representan constantes de cadena, nombres de clase, interfaces, campos, métodos y otras constantes que se encuentran en la estructura de ClassFile y sus subestructuras. Cada elemento de agrupación constante comienza con una etiqueta de un solo byte que define el tipo de constante. Dependiendo del tipo de constante, los siguientes bytes pueden ser un valor constante inmediato o una referencia a otro elemento en el grupo.
Banderas de acceso. Una lista de indicadores que indican que la clase es una interfaz, pública o privada, la clase final o no. Varios indicadores como
ACC_PUBLIC
,
ACC_FINAL
,
ACC_INTERFACE
,
ACC_ENUM
, etc. se describen en la Especificación de máquina virtual Java.
Esta clase Enlace a la entrada en el grupo constante.
Super clase. Enlace a la entrada en el grupo constante.
Interfaces El número de interfaces implementadas por la clase.
El número de campos. El número de campos en la clase o interfaz.
Campos. Después del número de campos, sigue una tabla de estructuras de longitud variable. Uno para cada campo con una descripción del tipo y nombre del campo (con referencia al grupo de constantes).
Número de métodos El número de métodos en la clase o interfaz. Este número incluye solo métodos que se definen explícitamente en la clase, sin métodos heredados de superclases.
Métodos Los siguientes son los métodos mismos. Para cada método, se incluye la siguiente información: el descriptor del método (tipo de retorno y lista de argumentos), el número de palabras necesarias para las variables locales del método, el número máximo de palabras de pila necesarias para la pila de operandos del método, la tabla de excepciones capturada por el método, los códigos de bytes del método y la tabla números de línea
El número de atributos. El número de atributos en esta clase, interfaz o módulo.
Atributos El número de atributos es seguido por tablas o estructuras de longitud variable que describen cada atributo. Por ejemplo, siempre hay un atributo "SourceFile". Contiene el nombre del archivo fuente desde el que se compiló el archivo de clase.
Aunque el archivo de clase no es directamente legible por humanos, hay una herramienta en el JDK llamada
javap que muestra su contenido en un formato conveniente.
Escribamos un programa Java simple como se muestra a continuación.
package bytecode; import java.io.Serializable; public class HelloWorld implements Serializable, Cloneable { public static void main(String[] args) { System.out.println("Hello World"); } }
Compilemos este programa con
javac
, que creará el archivo
HelloWorld.class
, y usaremos
javap
para ver el archivo
HelloWorld.class
. Ejecutar
javap
con la opción
-v (verbose)
para
HelloWorld.class
da el siguiente 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
Aquí puede ver que la clase es pública y tiene 37 entradas en el grupo constante. Hay un atributo (SourceFile a continuación), la clase implementa dos interfaces (Serializable, Cloneable), no tiene campos y hay dos métodos.
Es posible que haya notado que solo hay un método principal estático en el código fuente, pero el archivo de clase dice que hay dos métodos. Recuerde el constructor predeterminado: este es un constructor sin argumentos agregado por el compilador
javac
, cuyo código de bytes también es visible en la salida. Los constructores se consideran métodos.
Puedes leer más sobre javap
aquí .
Consejo : también puede usar javap para ver cómo las lambdas difieren de las clases internas anónimas.