Ответ 1
javac
будет представлен байт-код, который является точным представлением исходной Java-программы, которая генерирует байт-код (за исключением некоторых ситуаций, когда он может оптимизировать: постоянное сгибание и удаление мертвого кода). Однако оптимизация может выполняться JVM, когда он использует компилятор JIT.
Для первого сценария похоже, что JVM поддерживает inlining (см. ниже в разделе Методы и см. здесь для примера inline для JVM).
Я не мог найти никаких примеров применения метода, выполняемого самим javac
. Я попробовал скомпилировать несколько примеров программ (похожих на тот, который вы описали в своем вопросе), и ни один из них, казалось, напрямую не ввел метод, даже когда он был final
. Казалось бы, такие оптимизаторы выполняются JVM JIT-компилятором, а не javac
. "Компилятор", упомянутый в разделе Методы здесь, кажется, является компилятором JSM JIT для HotSpot, а не javac
.
Из того, что я вижу, javac
поддерживает устранение мертвого кода (см. пример для второго случая) и постоянную фальцовку. При постоянном складывании компилятор будет предварительно вычислять константные выражения и использовать вычисляемое значение вместо выполнения вычисления во время выполнения. Например:
public class ConstantFolding {
private static final int a = 100;
private static final int b = 200;
public final void baz() {
int c = a + b;
}
}
компилируется следующий байт-код:
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
}
Обратите внимание, что байт-код имеет sipush 300
вместо aload
getfield
и iadd
. 300
- вычисленное значение. Это также относится к переменным private final
. Если a
и b
не были статичными, итоговый байт-код будет выглядеть следующим образом:
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
}
Здесь также используется sipush 300
.
Во втором случае (удаление мертвого кода) я использовал следующую тестовую программу:
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";
}
}
который дает следующий байт-код:
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
}
Как вы можете видеть, foo
вообще не вызывается в baz
, потому что код внутри блока if
эффективно "мертв".
Sun (теперь Oracle) HotSpot JVM сочетает в себе интерпретацию байт-кода, а также компиляцию JIT. Когда байт-код представлен JVM, код изначально интерпретируется, но JVM будет контролировать байт-код и выделять часто используемые части. Он скрывает эти части в собственный код, чтобы они работали быстрее. Для части байт-кода, которые не используются так часто, эта компиляция не выполняется. Это так же хорошо, потому что компиляция имеет некоторые накладные расходы. Так что это действительно вопрос компромисса. Если вы решите скомпилировать весь байт-код в nativecode, тогда код может иметь очень долгую задержку запуска.
В дополнение к мониторингу байт-кода, JVM также может выполнять статический анализ байт-кода, поскольку он интерпретирует и загружает его для дальнейшей оптимизации.
Если вы хотите знать конкретные виды оптимизаций, которые выполняет JVM, эта страница в Oracle очень полезна. В нем описываются методы производительности, используемые в JVM HotSpot.