Articulo de referencia

Código de bytes de la JVM

El bytecode de la JVM es la arquitectura del conjunto de instrucciones (ISA) de la máquina virtual Java (JVM), el lenguaje al que se compila Java y otro código fuente compatible...

El bytecode de la JVM es la arquitectura del conjunto de instrucciones (ISA) de la máquina virtual Java (JVM), el lenguaje al que se compila Java y otro código fuente compatible con la JVM . [ 1 ] Cada instrucción está representada por un byte , de ahí el nombre bytecode , lo que lo convierte en una forma compacta de datos . [ 2 ]

Debido a la naturaleza de las máquinas virtuales y el código de bytes, un programa de código de bytes de JVM se puede ejecutar en cualquier máquina con una JVM compatible, sin el largo proceso de compilación desde el código fuente.

El código de bytes de la JVM se utiliza en tiempo de ejecución , ya sea interpretado por una JVM o compilado a código máquina mediante compilación justo a tiempo (JIT) y ejecutado como una aplicación nativa.

Como el código de bytes de la JVM está diseñado para la compatibilidad y seguridad de software multiplataforma , una aplicación de código de bytes de la JVM tiende a ejecutarse de manera consistente en diversas configuraciones de hardware y software . [ 3 ]

Relación con Java

En general, un programador Java no necesita comprender el código de bytes de la JVM ni siquiera conocerlo. Sin embargo, como se sugiere en la revista IBM developerWorks, "Comprender el código de bytes y qué código de bytes es probable que genere un compilador Java ayuda al programador Java de la misma manera que el conocimiento del lenguaje ensamblador ayuda al programador de C o C++ ". [ 4 ]

Arquitectura del conjunto de instrucciones

El código de bytes comprende varios tipos de instrucciones, incluyendo manipulación de datos, transferencia de control, creación y manipulación de objetos e invocación de métodos, todos ellos integrales al modelo de programación orientada a objetos de Java . [ 1 ]

La JVM es tanto una máquina de pila como una máquina de registro . Cada marco para una llamada a método tiene una "pila de operandos" y una matriz de "variables locales". [ 5 ] : 2.6 [ 2 ] La pila de operandos se utiliza para pasar operandos a cálculos y para recibir el valor de retorno de un método llamado, mientras que las variables locales cumplen la misma función que los registros y también se utilizan para pasar argumentos de método. El tamaño máximo de la pila de operandos y la matriz de variables locales, calculado por el compilador, forma parte de los atributos de cada método. [ 5 ] : 4.7.3 Cada uno puede tener un tamaño independiente de 0 a 65535 valores, donde cada valor es de 32 bits. longy doublelos tipos, que son de 64 bits, ocupan dos variables locales consecutivas [ 5 ] : 2.6.1 (que no necesitan estar alineadas a 64 bits en la matriz de variables locales) o un valor en la pila de operandos (pero se cuentan como dos unidades en la profundidad de la pila). [ 5 ] : 2.6.2

Conjunto de instrucciones

Cada código de bytes se compone de un byte que representa el código de operación , junto con cero o más bytes para los operandos. [ 5 ] : 2.11

De los 256 posibles códigos de operación de un byte de longitud , a partir de 2015, 202 están en uso (~79%), 51 están reservados para uso futuro (~20%) y 3 instrucciones (~1%) están permanentemente reservadas para que las implementaciones de JVM las utilicen. [ 5 ] : 6.2 Dos de estas ( impdep1y impdep2) son para proporcionar trampas para software y hardware específicos de la implementación, respectivamente. La tercera se utiliza para que los depuradores implementen puntos de interrupción.

Las instrucciones se dividen en varios grupos generales:

  • Cargar y almacenar (por ejemplo aload_0, istore)
  • Aritmética y lógica (por ejemplo ladd, fcmpl)
  • Conversión de tipo (por ejemplo i2b, d2i)
  • Creación y manipulación de objetos ( new, putfield)
  • Gestión de la pila de operandos (por ejemplo swap, dup2)
  • Transferencia de control (por ejemplo ifeq, goto)
  • Invocación y retorno de método (por ejemplo invokespecial, areturn)

También se incluyen algunas instrucciones para tareas más especializadas, como el lanzamiento de excepciones, la sincronización, etc.

Muchas instrucciones tienen prefijos y/o sufijos que hacen referencia a los tipos de operandos sobre los que operan. [ 5 ] : 2.11.1 Estos son los siguientes:

Por ejemplo, iaddsumará dos enteros, mientras que daddsumará dos números decimales. Las instrucciones const, load, y storetambién pueden tomar un sufijo de la forma , donde n es un número del 0 al 3 para y . El valor máximo de n para difiere según el tipo._nloadstoreconst

Las constinstrucciones insertan un valor del tipo especificado en la pila. Por ejemplo, iconst_5insertará un entero (valor de 32 bits) con el valor 5 en la pila, mientras que dconst_1insertará un doble (valor de punto flotante de 64 bits) con el valor 1 en la pila. También hay una instrucción aconst_nullque inserta una nullreferencia. La n para las instrucciones loady storeespecifica el índice en el array de variables locales desde donde cargar o almacenar. La aload_0instrucción inserta el objeto en la variable local 0 en la pila (este suele ser el thisobjeto). istore_1almacena el entero en la parte superior de la pila en la variable local 1. Para variables locales más allá de 3, se omite el sufijo y se deben usar operandos.

Ejemplo

Considere el siguiente código Java:

exterior : para ( int i = 2 ; i < 1000 ; i ++ ) { para ( int j = 2 ; j < i ; j ++ ) { si ( i % j == 0 ) continuar exterior ; } System.out.println ( i ) ; }

Un compilador de Java podría traducir el código Java anterior a código de bytes de la siguiente manera, suponiendo que lo anterior se colocó en un 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; // Campo java/lang/System.out : Ljava/io/PrintStream; 34 : iload_1 35 : invokevirtual #85 ; // Método java/io/PrintStream.println:(I)V 38 : iinc 1, 1 41 : ir a 2 44 : regresar

Generación

El lenguaje más común que se dirige a la máquina virtual Java mediante la generación de bytecode de JVM es Java. Originalmente, solo existía un compilador, el compilador javac de Sun Microsystems , que compilaba el código fuente de Java a bytecode de JVM; pero dado que ahora están disponibles todas las especificaciones para el bytecode de JVM, otras empresas han proporcionado compiladores que generan bytecode de JVM. Algunos ejemplos de otros compiladores son:

Algunos proyectos proporcionan ensambladores de Java para permitir la escritura manual del código de bytes de la JVM. El código ensamblador también puede ser generado por una máquina, por ejemplo, mediante un compilador dirigido a una máquina virtual Java. Entre los ensambladores de Java más destacados se incluyen:

  • Jasmin toma descripciones de texto para clases Java, escritas en una sintaxis simple similar al lenguaje ensamblador utilizando el conjunto de instrucciones de la máquina virtual Java y genera un archivo de clase Java [ 6 ].
  • Jamaica, un lenguaje ensamblador de macros para la máquina virtual Java. Se utiliza la sintaxis de Java para la definición de clases o interfaces. Los cuerpos de los métodos se especifican mediante instrucciones de código de bytes. [ 7 ]
  • Krakatau Bytecode Tools contiene actualmente tres herramientas: un descompilador y desensamblador para archivos de clase Java y un ensamblador para crear archivos de clase. [ 8 ]
  • Lilac, un ensamblador y desensamblador para la máquina virtual de Java. [ 9 ]

Otros han desarrollado compiladores, para diferentes lenguajes de programación, dirigidos a la máquina virtual de Java, tales como:

Ejecución

Actualmente existen varias máquinas virtuales Java disponibles para ejecutar el código de bytes de la JVM, tanto gratuitas como comerciales. Si no se desea ejecutar el código de bytes en una máquina virtual, un desarrollador también puede compilar el código fuente o el código de bytes de Java directamente a código máquina nativo con herramientas como el Compilador GNU para Java (GCJ). Algunos procesadores pueden ejecutar el código de bytes de la JVM de forma nativa. Dichos procesadores se denominan procesadores Java .

Compatibilidad con lenguajes dinámicos

La máquina virtual de Java proporciona cierto soporte para lenguajes de tipado dinámico . La mayor parte del conjunto de instrucciones de la JVM existente es de tipado estático , en el sentido de que las firmas de las llamadas a métodos se verifican en tiempo de compilación , sin un mecanismo para posponer esta decisión al tiempo de ejecución o para elegir el despacho del método mediante un enfoque alternativo. [ 12 ]

La solicitud de especificación de Java ( JSR ) 292 ( compatibilidad con lenguajes de tipado dinámico en la plataforma Java ) [ 13 ] añadió una nueva invokedynamicinstrucción a nivel de la JVM para permitir la invocación de métodos que dependen de la comprobación de tipos dinámica (en lugar de la instrucción existente de comprobación de tipos estática invokevirtual). La máquina Da Vinci es una implementación prototipo de máquina virtual que aloja extensiones de la JVM destinadas a admitir lenguajes dinámicos. Todas las JVM que admiten Java Platform, Standard Edition (JSE) 7 también incluyen el invokedynamiccódigo de operación.

Véase también

Referencias

  1. 1 2 "Especificación de la máquina virtual Java" . Oracle . Consultado el 14 de noviembre de 2023 .
  2. 1 2 Lindholm, Tim (2015). Especificación de la máquina virtual Java . Oracle. ISBN 978-0133905908.
  3. Arnold, Ken (1996). "El lenguaje de programación Java". Sun Microsystems . 1 (1): 30– 40.
  4. "IBM Developer" . developer.ibm.com . Consultado el 20 de febrero de 2006 .
  5. 1 2 3 4 5 6 7 Lindholm, Tim; Yellin, Frank; Bracha, Gilad; Buckley, Alex (13 de febrero de 2015). La especificación de la máquina virtual Java (Java SE 8 ed.). 
  6. "Página principal de Jasmin" . jasmin.sourceforge.net . Consultado el 2 de junio de 2024 .
  7. Huang, James Jianbo. "Jamaica: El ensamblador de macros de la máquina virtual Java (JVM)" . JavaWorld . Archivado del original el 14 de noviembre de 2023. Consultado el 2 de junio de 2024 .
  8. ^ "Cuentacuentos/Krakatau" . 1 de junio de 2024 . Recuperado 2 de junio de 2024 - vía GitHub.
  9. "Lilac - un ensamblador de Java" . lilac.sourceforge.net . Consultado el 2 de junio de 2024 .
  10. "Nuevas características de FPC 3.0.0" . Free Pascal Wiki . Consultado el 2 de junio de 2024 .
  11. "FPC JVM - Wiki de Free Pascal" . wiki.freepascal.org . Consultado el 2 de junio de 2024 .
  12. Nutter, Charles (3 de enero de 2007). "InvokeDynamic: ¿Realmente útil?" . Consultado el 25 de enero de 2008 .
  13. "El programa Java Community Process(SM) - JSRs: Java Specification Requests - detalle JSR# 292" . www.jcp.org . Consultado el 2 de junio de 2024 .
  • Especificación de la máquina virtual Java de Oracle
  • Lenguajes de programación para la máquina virtual Java
  • Visualizador de código de bytes: visor y depurador de código de bytes (complemento gratuito para Eclipse)
  • AdaptJ StackTrace: depuración a nivel de bytecode con control total de la pila, las variables locales y el flujo de ejecución.
  • Java Class Unpacker: complemento para Total Commander que permite abrir archivos de clase como archivos comprimidos y ver los campos y métodos como archivos. El código de bytes se puede visualizar como texto usando F3.