2011-04-26 14 views
12

Para este hilo Herbert Schildt escribe:¿Por qué JVM no compila todo el programa por adelantado, en lugar de compilarlo pieza por pieza?

Es importante entender que no es práctico para compilar todo un programa Java en código ejecutable a la vez, ya que Java realiza varias comprobaciones en tiempo de ejecución que se pueden hacer solo en tiempo de ejecución.

¿Qué verificaciones en tiempo de ejecución quiere decir?

Explique las razones para compilar el bytecode pieza por pieza, no el programa completo.

+0

No estoy del todo seguro de si esto se trata de '* .java -> * .class' o acerca de la compilación de JIT, ¿alguien aclararía? – delnan

+3

Esto es sobre la compilación de JIT. – Sergey

Respuesta

1

lo que está diciendo es que no es práctico para compilar todo el código de bytes de lenguaje de máquina en tiempo de ejecución. Podrías precompilar todo, pero ese no es el enfoque que toma JIT.

Por un lado, no se sabe qué tan grande es el programa. La gente se molestaría bastante con un inicio de 30 minutos, ya que compiló todas las bibliotecas que pudo encontrar (un programa Java dado no está en un solo archivo, tiene acceso a todo en el classpath)

Por otro, incluso si le dijo al sistema exactamente qué componentes usaría su programa, no se sabe cuánto de su programa podría usarse para una corrida determinada: la gente se molestaría más con un arranque de 30 minutos para ejecutar un programa de línea de comando con parámetros que consisten en " --ayuda "

Finalmente puede hacer grandes trucos mediante la compilación mientras se está ejecutando. con un método como este:

public testMeh(int param) { 
    if(param == 35) 
     do bunches of crap; 
    else if (param > 5) 
     do much more crap; 
    else 
     return; 
    } 

El compilador puede llamar una o dos veces y sobre la marcha reconocer que valores de 5 y bajo simplemente volver. Si esto se llama todo el tiempo con un valor de 2, puede reemplazar la llamada al método ENTER con if (param! = 2) testMeh (param);

que elimina toda la llamada de método para ese número. Más tarde se da cuenta de que no llamar a ese método significa que ciertas variables miembro no pueden cambiar y que puede colapsar otras partes del código a nada.

Esto es simplemente duro como el infierno si precompila cosas. Quiero decir que puedes escribir código de excepción en todas partes a medida que reconoces los patrones, pero tu código se convertirá rápidamente en una pesadilla.

Ahora, si está preguntando por qué no precompila todo el programa cuando lo compila en bytecode, esa es una pregunta diferente y no a qué se refería la cita, pero puede hacerlo. Ha sido hecho y funciona bastante bien. Cambia la portabilidad y la flexibilidad de tiempo de ejecución para un tiempo de inicio más rápido.

+0

Fantástica respuesta, con ejemplos prácticos. – Keith

2

Ese razonamiento es incorrecto. Es posible compilar todo un programa Java en código ejecutable, todo de una vez, y de hecho gcj hace exactamente eso.

Las razones reales entornos de ejecución Java normalmente no lo hacen por adelantado compilación:

  • Como parte de Java de "escribir una vez, ejecutar en cualquier lugar" filosofía, código compilado se distribuye de la plataforma de código de bytes independiente en lugar de la plataforma -código nativo específico.

  • La compilación de códigos de bytes al código nativo al inicio no es óptima por algunas razones. En primer lugar, incurriría en un costo de puesta en marcha considerable, especialmente para programas más grandes. Además, los JIT modernos pueden utilizar el perfil de tiempo de ejecución para producir mejores optimizaciones de las que serían posibles con un compilador inicial. Las optimizaciones generalmente implican hacer intercambios: por ejemplo, la línea interna elimina la sobrecarga de llamada de función a expensas de un código más grande (que a su vez puede conducir a un código más lento debido al mal rendimiento de la memoria caché o incluso al intercambio). Cuando tiene información de perfiles de tiempo de ejecución en tiempo de compilación, puede decidir de forma más inteligente qué concesiones tienen sentido.

  • Las API de Java tienen un historial de confiar en la capacidad de cargar bytecode en el tiempo de ejecución. (Piense en cualquier cosa que dependa de Class.forName y/o cargadores de clases personalizados, por ejemplo, incluyendo applets, contenedores de servlets, etc.) Para admitir esto, es necesario algún tipo de compilador (o intérprete) en tiempo de ejecución. Hacer una compilación inicial significaría que tendría que eliminar el soporte para esto o tratarlo como un caso especial.

+3

¡Posible! = Práctico. No sé qué código genera gcj, pero debo suponer que está lleno de llamadas a funciones que realizan estas comprobaciones o abultadas con versiones en línea de estas pruebas. El código de JIT puede simplemente asumir que todo funciona y dejar una pequeña guardia, que generalmente es mucho más eficiente. – delnan

+1

@delnan: Lo siento, fui llamado por el mundo real antes de tener la oportunidad de escribir la segunda mitad de mi respuesta. Mi punto era sobre el razonamiento utilizado, no la conclusión. Un JIT no tiene acceso a la magia. Cualquier clase de guardia que un JIT podría usar también podría ser utilizada por código compilado por adelantado, por lo que el argumento de que no es práctico compilar Java por adelantado porque "Java realiza varias comprobaciones de tiempo de ejecución" es defectuoso. Hay buenas razones por las cuales Java no se compila por adelantado, pero las verificaciones en el tiempo de ejecución son un misterio. –

3

Podría haber varias razones para compilar pieza por pieza (esos son los dos primeros que vienen a la mente):

  1. Optimización de un código que se utiliza muchas veces, no todos el código necesita ser recompilado, pero solo la parte específica.
  2. Adquisición de clases a través de la red: a veces desea evitar adquirir todo el código, ya que cuesta en ancho de banda.

Y yo no sé si este sitio es exacta, pero he aprendido mucho de ella: http://www.artima.com/insidejvm/ed2/index.html

2

Una posible razón: el tiempo de ejecución de Java es entorno muy dinámico. Las clases se cargan y descargan todo el tiempo. Dependiendo de las clases actualmente en uso, ciertas optimizaciones son posibles o imposibles. Por ejemplo, si tiene la interfaz A y la implementación única ImplA, todas las llamadas a los métodos en la interfaz A pueden cambiarse para usar llamadas directas a métodos ImplA, sin hacer ningún método virtual. Una vez que se carga otra clase AnotherImplA, este tipo de optimización ya no es posible.Una vez que se descarga Another AnotherImplA, es posible de nuevo.

Hay muchas optimizaciones en JVM que usan datos recopilados durante el tiempo de ejecución. Hacer todas las optimizaciones desde el principio dejaría pasar muchas oportunidades.

Check también reciente charla del acantilado Click, A JVM Does That?

0

Tiempos de arranque más rápidos y para poder realizar optimizaciones más inteligentes después del perfilado del código durante la ejecución.

Este artículo proporciona una excelente respuesta a esta pregunta: https://advancedweb.hu/2016/05/27/jvm_jit_optimization_techniques/

La JVM se inicia a cabo la interpretación de todo en la marcha. Realiza un seguimiento de cuántas veces se llama a una función y si pasa un cierto límite compila esa función y almacena en caché este código de máquina. Cada vez que se llama a esa función después de que ejecuta este código de máquina. Si pasa otro umbral, puede compilarse nuevamente, pero con optimizaciones aún más agresivas.

El objetivo es equilibrar el costo inicial de la compilación. Compilar todo por adelantado causará tiempos de inicio más largos y el compilador no puede realizar las mejores optimizaciones posibles sin ver el código ejecutado. Los programas de ejecución más larga tienen el potencial de mejores optimizaciones que los programas de ejecución más cortos.

También puede compilar solo el cuerpo de un bucle si se ejecuta tantas veces.

Cuestiones relacionadas