¿Cómo funcionan los compiladores Java AOT?


18

Existen algunas herramientas ( Excelsior JET , etc.) que afirman transformar las aplicaciones Java en ejecutables nativos ( *.exe). Sin embargo, entiendo que estas herramientas realmente solo están creando envoltorios nativos que invocan / ejecutan javadesde un shell o línea de comandos.

Si esa comprensión es incorrecta, no veo cómo podría ser. Si un JVM ( javaproceso) en ejecución es esencialmente un intérprete de alto rendimiento, cargando bytecode de archivos de clase Java sobre la marcha, entonces no veo cómo una aplicación Java (una colección de archivos de bytecode que sirven como entrada a una JVM) podría ser verdaderamente convertido en un ejecutable.

Esto se debe a que el proceso JVM ya es un ejecutable nativo que toma conjuntos de archivos de bytecode como entrada. No parece posible fusionar esos archivos de bytecode y el proceso JVM en un único ejecutable nativo unificado sin reescribir por completo la JVM y desviarse de la especificación JVM.

Entonces pregunto: ¿cómo estas herramientas "transforman" realmente los archivos de clase Java en un ejecutable nativo, o no?

Respuestas:


26

Todos los programas tienen un entorno de tiempo de ejecución. Tendemos a olvidar esto, pero está ahí. Lib estándar para C que envuelve las llamadas del sistema al sistema operativo. Objective-C tiene su tiempo de ejecución que envuelve todo el paso de mensajes.

Con Java, el tiempo de ejecución es la JVM. La mayoría de las implementaciones de Java con las que la gente está familiarizada son similares al HotSpot JVM, que es un intérprete de código de bytes y un compilador JIT.

Esta no tiene que ser la única implementación. No hay absolutamente nada que diga que no puede construir un tiempo de ejecución lib-esque estándar para Java y compilar el código en código máquina nativa y ejecutarlo dentro del tiempo de ejecución que maneja las llamadas de nuevos objetos en mallocs y el acceso a archivos en llamadas del sistema en la máquina. Y eso es lo que hace el compilador Ahead Of Time (AOT en lugar de JIT). Llama a ese tiempo de ejecución lo que va ... que se podría llamar una implementación JVM (y que no sigue la especificación JVM) o un entorno de tiempo de ejecución o lib estándar de Java. Está allí y hace esencialmente lo mismo.

Se podría hacer reimplementando javacpara apuntar a la máquina nativa (eso es lo que hizo GCJ ). O podría hacerse traduciendo el código de byte generado por el código de javacmáquina (o byte) para otra máquina, eso es lo que hace Android. Según Wikipedia, eso es lo que también hace Excelsior JET ("El compilador transforma el código de bytes de Java portátil en ejecutables optimizados para el hardware y sistema operativo (SO) deseado"), y lo mismo es cierto para RoboVM .

Hay complicaciones adicionales con Java que significa que esto es muy difícil de hacer como un enfoque exclusivo. La carga dinámica de clases ( class.forName()) u objetos proxy requiere una dinámica que los compiladores de AOT no proporcionan fácilmente y, por lo tanto, sus respectivas JVM también deben incluir un compilador JIT (Excelsior JET) o un intérprete (GCJ) para manejar clases que no se pueden precompilar nativo.

Recuerde, la JVM es una especificación , con muchas implementaciones . La biblioteca estándar C también es una especificación con muchas implementaciones diferentes.

Con Java8, se ha trabajado bastante en la compilación AOT. En el mejor de los casos, uno solo puede resumir AOT en general dentro de los límites del cuadro de texto. Sin embargo, en la JVM Language Summit para 2015 (agosto de 2015), hubo una presentación: Java Goes AOT (video de youtube). Este video dura 40 minutos y abarca muchos de los aspectos técnicos más profundos y puntos de referencia de rendimiento.


Lo siento, no sé mucho sobre esto, pero ¿significa esto que Java es nativo ahora? ¿O significa que hay un nuevo indicador de compilador que nos permite compilar programas de Java en código nativo si lo deseamos, y todavía tenemos la opción de compilar en código de bytes?
Pavel

@ paulpaul1076 Sugeriría ver el video que vinculé. Hay bastante más de lo que razonablemente puedo incluir en un comentario.

4

gcj ejemplo ejecutable mínimo

También puede observar una implementación de código abierto como gcj(ahora obsoleta). Por ejemplo, archivo Java:

public class Main {
    public static void main(String args[]) {
        System.out.println("hello world");
    }
}

Luego compila y ejecuta con:

gcj -c Main.java
gcj --main=Main -o Main Main.o
./Main

Ahora eres libre de descompilarlo y ver cómo funciona.

file Main.o dice que es un archivo elfo.

readelf -d Main | grep NEEDED dice que depende de las bibliotecas dinámicas:

0x0000000000000001 (NEEDED)             Shared library: [libgcj.so.14]
0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

Entonces libgcj.so debe ser donde se implementa la funcionalidad Java.

Luego puede descompilarlo con:

objdump -Cdr Main.o

y ver exactamente cómo se implementa.

Se parece mucho a C ++, muchas alteraciones de nombres y llamadas indirectas a funciones polimórficas.

Me pregunto cómo se iniciará la recolección de basura. Merecería la pena examinarlo: /programming/7100776/garbage-collection-implementation-in-compiled-languages y otros lenguajes compilados con GC como Go.

Probado en Ubuntu 14.04, GCC 4.8.4.

También eche un vistazo a https://en.wikipedia.org/wiki/Android_Runtime , la columna vertebral de Android 5 en adelante, que hace todo lo posible para optimizar las aplicaciones de Android.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.