При компиляции следующего кода с простым блоком try/finally
компилятор Java выводит результат ниже (просматривается в ASM Bytecode Viewer):
Код:
try
{
System.out.println("Attempting to divide by zero...");
System.out.println(1 / 0);
}
finally
{
System.out.println("Finally...");
}
Bytecode:
TRYCATCHBLOCK L0 L1 L1
L0
LINENUMBER 10 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Attempting to divide by zero..."
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L2
LINENUMBER 11 L2
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ICONST_1
ICONST_0
IDIV
INVOKEVIRTUAL java/io/PrintStream.println (I)V
L3
LINENUMBER 12 L3
GOTO L4
L1
LINENUMBER 14 L1
FRAME SAME1 java/lang/Throwable
ASTORE 1
L5
LINENUMBER 15 L5
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Finally..."
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L6
LINENUMBER 16 L6
ALOAD 1
ATHROW
L4
LINENUMBER 15 L4
FRAME SAME
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Finally..."
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L7
LINENUMBER 17 L7
RETURN
L8
LOCALVARIABLE args [Ljava/lang/String; L0 L8 0
MAXSTACK = 3
MAXLOCALS = 2
При добавлении блока catch
между ними я заметил, что компилятор скопировал блок finally
3 (не отправляя снова байт-код). Это кажется пустой тратой пространства в файле класса. Копирование также не ограничивается максимальным количеством инструкций (подобно тому, как работает инлайн), поскольку он даже дублировал блок finally
, когда я добавил больше вызовов на System.out.println
.
Однако результат использования собственного компилятора, который использует другой подход для компиляции одного и того же кода, работает точно так же, когда он выполняется, но требует меньше места, используя инструкцию GOTO
:
public static main([Ljava/lang/String;)V
// parameter args
TRYCATCHBLOCK L0 L1 L1
L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Attempting to divide by zero..."
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ICONST_1
ICONST_0
IDIV
INVOKEVIRTUAL java/io/PrintStream.println (I)V
GOTO L2
L1
FRAME SAME1 java/lang/Throwable
POP
L2
FRAME SAME
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Finally..."
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L3
RETURN
LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
MAXSTACK = 3
MAXLOCALS = 1
Почему компилятор Java (или компилятор Eclipse) копирует байт-код блока finally
несколько раз, даже используя athrow
для повторного исключения исключений, когда одна и та же семантика может быть достигнута с помощью GOTO
? Является ли эта часть процесса оптимизации или мой компилятор делает это неправильно?
(Выход в обоих случаях равен...)
Attempting to divide by zero...
Finally...