次のコードを単純な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...");
}
バイトコード:
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
間にブロックを追加すると、コンパイラーがブロックを3catch
回コピーしたことに気付きました(バイトコードを再投稿していません)。これは、クラス ファイル内のスペースの無駄のようです。への呼び出しをさらに追加したときにブロックを複製したため、コピーも最大命令数に制限されていないようです(インライン化の仕組みと同様)。finally
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...