retornan - que es un objeto en java
¿Sería Java método(s) en línea durante la optimización? (6)
Me pregunto si JVM / javac es lo suficientemente inteligente como para activar
// This line...
string a = foo();
string foo()
{
return bar();
}
string bar()
{
return some-complicated-string computation;
}
dentro
string a = bar();
O elimine la llamada innecesaria a foo () en el estuche de lanzamiento (porque el código inalcanzable):
string a = foo(bar());
// bar is the same
...
string foo(string b)
{
if (debug) do-something-with(b);
}
Mi sensación es sí para el primer ejemplo y "no estoy tan seguro" para el segundo, pero ¿alguien podría darme algunos enlaces para confirmarlo?
La JVM muy probablemente estará en línea. En general, es mejor optimizar para la legibilidad humana. Deje que la JVM realice la optimización de tiempo de ejecución.
El experto en JVM, this final
no tiene ningún impacto en los métodos que están en línea.
Podría estar equivocado, pero mi sensación es "no en todos los casos". Debido a que su string bar()
puede ser anulada por otras clases en el mismo paquete. final
métodos final
son buenos candidatos, pero depende de JIT.
Otra nota interesante está here .
Si lanzas una excepción en bar () e imprimes stacktrace verás todo el camino de las llamadas ... Creo que java los honra a todos.
El segundo caso es el mismo, la depuración es solo una variable de su sistema, no una definición como en C ++, por lo que es obligatorio evaluarlo antes.
Un compilador JIT "altamente optimizador" alineará ambos casos (y, @Mysticial, podría incluso incluir algunos casos polimórficos, empleando varias formas de engaño).
Puede aumentar las posibilidades de alineación haciendo que los métodos sean definitivos y algunos otros trucos.
javac realiza una redacción primitiva, en su mayoría de métodos finales / privados, principalmente para ayudar a algunos paradigmas de compilación condicional.
en el mismo archivo de clase, el javac podrá alinearse static
y final
(otros archivos de clase podrían cambiar la función en línea)
sin embargo, el JIT podrá optimizar mucho más (incluida la delimitación de límites de eliminación superfluos y nulos, etc.) porque sabe más sobre el código
javac
presentará bytecode que es una fiel representación del programa original de Java que generó el bytecode (excepto en ciertas situaciones en las que puede optimizar: plegado constante y eliminación de código muerto ). Sin embargo, la JVM puede realizar la optimización cuando utiliza el compilador JIT.
Para el primer escenario, parece que la JVM admite la creación de líneas (ver en Métodos here y ver here un ejemplo de entrada en la JVM).
No pude encontrar ningún ejemplo de método en línea realizado por javac
. Intenté compilar algunos programas de muestra (similares a los que describiste en tu pregunta) y ninguno de ellos pareció alinear directamente el método, incluso cuando era final
. Parece que este tipo de optimizaciones las realiza el compilador JIT de la JVM y no el javac
. El "compilador" mencionado en Métodos here parece ser el compilador JIT de HotSpot JVM y no javac
.
Por lo que puedo ver, javac
admite eliminación de código muerto (ver el ejemplo para el segundo caso) y plegado constante . En el plegado constante, el compilador calculará previamente expresiones constantes y usará el valor calculado en lugar de realizar el cálculo durante el tiempo de ejecución. Por ejemplo:
public class ConstantFolding {
private static final int a = 100;
private static final int b = 200;
public final void baz() {
int c = a + b;
}
}
compila al siguiente bytecode:
Compiled from "ConstantFolding.java"
public class ConstantFolding extends java.lang.Object{
private static final int a;
private static final int b;
public ConstantFolding();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public final void baz();
Code:
0: sipush 300
3: istore_1
4: return
}
Tenga en cuenta que bytecode tiene un sipush 300
lugar de aload
s de getfield
y un iadd
. 300
es el valor calculado. Este es también el caso de private final
variables private final
. Si a
y b
no son estáticos, el bytecode resultante será:
Compiled from "ConstantFolding.java"
public class ConstantFolding extends java.lang.Object{
private final int a;
private final int b;
public ConstantFolding();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 100
7: putfield #2; //Field a:I
10: aload_0
11: sipush 200
14: putfield #3; //Field b:I
17: return
public final void baz();
Code:
0: sipush 300
3: istore_1
4: return
}
Aquí también, se usa un sipush 300
.
Para el segundo caso (eliminación de código muerto), utilicé el siguiente programa de prueba:
public class InlineTest {
private static final boolean debug = false;
private void baz() {
if(debug) {
String a = foo();
}
}
private String foo() {
return bar();
}
private String bar() {
return "abc";
}
}
que da el siguiente bytecode:
Compiled from "InlineTest.java"
public class InlineTest extends java.lang.Object{
private static final boolean debug;
public InlineTest();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
private void baz();
Code:
0: return
private java.lang.String foo();
Code:
0: aload_0
1: invokespecial #2; //Method bar:()Ljava/lang/String;
4: areturn
private java.lang.String bar();
Code:
0: ldc #3; //String abc
2: areturn
}
Como puede ver, el foo
no se llama en absoluto porque el código dentro del bloque if
está efectivamente "muerto".
La HotSpot JVM de Sun (ahora Oracle) combina la interpretación del bytecode y la compilación JIT. Cuando se presenta bytecode en la JVM, el código se interpreta inicialmente, pero la JVM supervisará el bytecode y seleccionará las partes que se ejecutan con frecuencia. Oculta estas partes en código nativo para que corran más rápido. Para un bytecode que no se usa con tanta frecuencia, esta compilación no está hecha. Esto es igual de bueno porque la compilación tiene algunos gastos generales. Entonces, realmente es una cuestión de compensación. Si decide compilar todos los códigos de bytes en código nativo, entonces el código puede tener un retraso de arranque muy largo.
Además de monitorear el bytecode, la JVM también puede realizar un análisis estático del bytecode mientras lo interpreta y lo carga para realizar más optimizaciones.
Si desea conocer los tipos específicos de optimizaciones que realiza la JVM, here de Oracle es bastante útil. Describe las técnicas de rendimiento utilizadas en HotSpot JVM.