2016-11-17 8 views
1

ASMified Javaバイトコードの変数の位置付けを理解できない。私は、次のJavacodeがあります最終的にJVMバイトコード内のローカル変数位置を理解する

mainため、次のバイトコードになり
public class TryCatch { 
    public static void main(String[] args) { 
     String test1 = null; 
     try { 
      String test2 ="try-inside-begin"; 
      System.out.println("try-outside-begin"); 
      try { 
       System.out.println(test2); 
       System.out.println(test1.length()); 
       System.out.println("try-inside-end"); 
      } catch (NullPointerException e) { 
       test2 = "catch-inside: " + e.getMessage(); 
       throw new Exception(test2, e); 
      } 
      System.out.println("try-outside-end"); 
     } catch (Exception e) { 
      System.out.println("catch-outside: " + e.getMessage()); 
     } finally { 
      System.out.println("finally"); 
     } 
    } 
} 

ASTORE 4/ALOAD 4があるどのように底部近く

TRYCATCHBLOCK L0 L1 L2 java/lang/NullPointerException 
    TRYCATCHBLOCK L3 L4 L5 java/lang/Exception 
    TRYCATCHBLOCK L3 L4 L6 null 
    TRYCATCHBLOCK L5 L7 L6 null 
    TRYCATCHBLOCK L6 L8 L6 null 
L9 
    LINENUMBER 5 L9 
    ACONST_NULL 
    ASTORE 1 
L3 
    LINENUMBER 7 L3 
    LDC "try-inside-begin" 
    ASTORE 2 
L10 
    LINENUMBER 8 L10 
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 
    LDC "try-outside-begin" 
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V 
L0 
    LINENUMBER 10 L0 
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 
    ALOAD 2 
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V 
L11 
    LINENUMBER 11 L11 
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 
    ALOAD 1 
    INVOKEVIRTUAL java/lang/String.length()I 
    INVOKEVIRTUAL java/io/PrintStream.println (I)V 
L12 
    LINENUMBER 12 L12 
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 
    LDC "try-inside-end" 
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V 
L1 
    LINENUMBER 16 L1 
    GOTO L13 
L2 
    LINENUMBER 13 L2 
FRAME FULL [[Ljava/lang/String; java/lang/String java/lang/String] [java/lang/NullPointerException] 
    ASTORE 3 
L14 
    LINENUMBER 14 L14 
    NEW java/lang/StringBuilder 
    DUP 
    INVOKESPECIAL java/lang/StringBuilder.<init>()V 
    LDC "catch-inside: " 
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; 
    ALOAD 3 
    INVOKEVIRTUAL java/lang/NullPointerException.getMessage()Ljava/lang/String; 
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; 
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String; 
    ASTORE 2 
L15 
    LINENUMBER 15 L15 
    NEW java/lang/Exception 
    DUP 
    ALOAD 2 
    ALOAD 3 
    INVOKESPECIAL java/lang/Exception.<init> (Ljava/lang/String;Ljava/lang/Throwable;)V 
    ATHROW 
L13 
    LINENUMBER 17 L13 
FRAME SAME 
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 
    LDC "try-outside-end" 
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V 
L4 
    LINENUMBER 21 L4 
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 
    LDC "finally" 
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V 
L16 
    LINENUMBER 22 L16 
    GOTO L17 
L5 
    LINENUMBER 18 L5 
FRAME FULL [[Ljava/lang/String; java/lang/String] [java/lang/Exception] 
    ASTORE 2 
L18 
    LINENUMBER 19 L18 
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 
    NEW java/lang/StringBuilder 
    DUP 
    INVOKESPECIAL java/lang/StringBuilder.<init>()V 
    LDC "catch-outside: " 
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; 
    ALOAD 2 
    INVOKEVIRTUAL java/lang/Exception.getMessage()Ljava/lang/String; 
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; 
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String; 
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V 
L7 
    LINENUMBER 21 L7 
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 
    LDC "finally" 
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V 
L19 
    LINENUMBER 22 L19 
    GOTO L17 
L6 
    LINENUMBER 21 L6 
FRAME SAME1 java/lang/Throwable 
    ASTORE 4 
L8 
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 
    LDC "finally" 
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V 
    ALOAD 4 
    ATHROW 
L17 
    LINENUMBER 23 L17 
FRAME SAME 
    RETURN 
    MAXSTACK = 4 
    MAXLOCALS = 5 

注意してください。なぜ3の代わりに4がありますか? SAME1フレームは「前のフレームと同じローカルであり、スタック上に単一の値を持つ」ため、前のフレームには2つのローカルのみがあります(参照:FRAME FULL [[Ljava/lang/String; java/lang/String] [java/lang/Exception])。

私はthe specを読んでいると、それはない3

+0

3の以前の使用が4を使用し、3を再使用しない方が簡単であることを除いて、3になると思われます。 –

+1

このスペック部分は古くなった 'jsr' /' ret'ベースのメカニズムです。一般に、コンパイラは、不要な追加のローカル変数を必要に応じて使用することができます(ここで説明します)(http://stackoverflow.com/a/25746587/2711488)、[ここ](http://stackoverflow.com/questions)/6386917/2711488)、 'javac'で生成された例外処理コードは最適ではありません。 – Holger

+0

@Holger - これは、私がバイトコード通訳を構築している場合、私は地元のvarsに頼ることができないことを意味すると思いますか?例えば。現在のところ、3つのローカル変数にある実行中のフレームは、5番目のスロットを尋ね、無視します4. :-( –

答えて

2

である理由が、それはそこから私にははっきりしていないいずれかのスタックフレームは、ローカル変数の状態と、それはを出現地点でオペランドスタックを説明しています。後の説明はもちろん、普通のようなものを変更することができます。あなたが正しく識別すると、L6のスタックフレームは、制御フローがL6に達すると、2つのローカル変数があることを示します。次の命令はスロット4に格納されますが、これは完全に合法です。

スタックマップの目的を理解するのに役立ちます。もともと、スタックマップは全く存在せず、検証者は推論を使用してメソッド内のすべてのポイントでローカル変数を計算しました。制御フローに遭遇すると、その時点の値をマージし、収束するまで反復する。

残念ながら、これは遅かったので、高速化のためにOracleはスタックマップを追加しました。これは、本質的に、制御フローが結合される任意の点で検証結果を事前計算する。このようにして、制御フローは結果を変更しないため、ベリファイアはコードを1回通過することができます。ベリファイアが制御フローに出会うと、現在の状態がジャンプターゲットで宣言されたスタックフレームと一致するかどうかをチェックし、そうでない場合はエラーをスローします。線形コードのセクションでは、検証者がこれまでと同じことをやり遂げることができるので、明らかにスタックフレームを含める必要はありません。

スタックフレームはデバッグ用ではなく、検証を高速化するためのもので、検証に必要な最小限の情報が含まれています。コンパイラがの命令にスタックフレームを挿入すると、astore 4の後のスタックフレームはもちろん4番目のスロットに新しい変数を表示します。

なぜスロット4を使用したのかは、スロット3を使用している可能性がありますが、これはコンパイラの気まぐれです。おそらくjavacの実装を単純化したのかもしれませんが、それはちょうど推測です。

+0

最後の段落は私が後にしたものです。なぜ彼らは3の代わりに4番のスロットを選んだのですか。ありがとう。 –

関連する問題