テキストは、Javaバイトコードを読んで

序文

Java言語の継続的な発展に伴い、Javaアプリケーションシナリオはゆっくりと優雅な問題解決手法の多様性を拡大していることも、このようなAOP技術として、導出された、Javaランタイム原理の明確な理解が必要となり、この記事の焦点は、Javaを説明しますバイトコードの知識。

基本バイトコード

コンパイラのクラスのバイトコードファイルによって生成されたJavaファイルは、バイトコードファイルには、ここでは詳細に進んで自分自身を持ってJavaツールから直接見ていない、独自のファイル形式を持っています。まず、次のように私たちは、クラスファイルをテストします。

public class Person {

	public String name;
	public int age;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}
复制代码

プロパティの名前と年齢を持つPersonクラスには、コンパイラは次のようにコマンドがあるダンプ、直接Javaクラスファイル、このツールのダンプを使用してPerson.classファイルを生成し、定義します。

javap -v -p Person.class
复制代码

次のようにダンプが生成されました:

public class com.sec.resourceparse.Person
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#27         // java/lang/Object."<init>":()V
   #2 = Fieldref           #4.#28         // com/sec/resourceparse/Person.name:Ljava/lang/String;
   #3 = Fieldref           #4.#29         // com/sec/resourceparse/Person.age:I
   #4 = Class              #30            // com/sec/resourceparse/Person
   #5 = Class              #31            // java/lang/Object
   #6 = Utf8               name
   #7 = Utf8               Ljava/lang/String;
   #8 = Utf8               age
   #9 = Utf8               I
  #10 = Utf8               <init>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Utf8               LineNumberTable
  #14 = Utf8               LocalVariableTable
  #15 = Utf8               this
  #16 = Utf8               Lcom/sec/resourceparse/Person;
  #17 = Utf8               getName
  #18 = Utf8               ()Ljava/lang/String;
  #19 = Utf8               setName
  #20 = Utf8               (Ljava/lang/String;)V
  #21 = Utf8               getAge
  #22 = Utf8               ()I
  #23 = Utf8               setAge
  #24 = Utf8               (I)V
  #25 = Utf8               SourceFile
  #26 = Utf8               Person.java
  #27 = NameAndType        #10:#11        // "<init>":()V
  #28 = NameAndType        #6:#7          // name:Ljava/lang/String;
  #29 = NameAndType        #8:#9          // age:I
  #30 = Utf8               com/sec/resourceparse/Person
  #31 = Utf8               java/lang/Object
{
  public java.lang.String name;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC

  public int age;
    descriptor: I
    flags: ACC_PUBLIC

  public com.sec.resourceparse.Person();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/sec/resourceparse/Person;

  public java.lang.String getName();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field name:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 9: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/sec/resourceparse/Person;

  public void setName(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: putfield      #2                  // Field name:Ljava/lang/String;
         5: return
      LineNumberTable:
        line 13: 0
        line 14: 5
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       6     0  this   Lcom/sec/resourceparse/Person;
            0       6     1  name   Ljava/lang/String;
复制代码

ここでは簡単に最初のを見て、一部を傍受し、そのような情報の紹介です。

public class com.sec.resourceparse.Person
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
复制代码

クラス名、コンパイラのJDKのバージョンと同様に、アクセス修飾子
、文字列プール:

Constant pool:
   #1 = Methodref          #5.#27         // java/lang/Object."<init>":()V
   #2 = Fieldref           #4.#28         // com/sec/resourceparse/Person.name:Ljava/lang/String;
   #3 = Fieldref           #4.#29         // com/sec/resourceparse/Person.age:I
   #4 = Class              #30            // com/sec/resourceparse/Person
   #5 = Class              #31            // java/lang/Object
   #6 = Utf8               name
   #7 = Utf8               Ljava/lang/String;
复制代码

ここでは内部の文字列を含む全体のクラスは、クラス情報、属性などの宣言が含まれ
最後に、情報の方法を:

 public java.lang.String getName();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field name:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 9: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/sec/resourceparse/Person;
复制代码

ここでの主なメソッド名、アクセス修飾子、情報および業務執行スタックの流れされ
、ここで、クラスファイルのクラス全体を読んだ後は、関連するバイトコードの基本です。

アクセス修飾子

次のように両方のフラグ情報でバイトコードのクラス、プロパティ、メソッドの上に、これは変性剤、およびアクセス修飾子に対応する値であるバイトコード・クラスです。

識別子名 識別子の値 定義
ACC_PUBLIC 0x0001 Public種類
ACC_FINAL 0x0010 決勝タイプ
ACC_SUPER 0x0020に 新しいバイトコード命令invokespecialセマンティクスの使用を許可します
ACC_INTERFACE 0x0200 インターフェイスの修飾子
ACC_ABSTRACT 0x0400 abstract修飾子
ACC_SYNTHETIC 0x1000番地 このフラグは、ユーザコードクラスによって生成されていません
ACC_ANNOTATION 0x2000で メモ修飾子
** ACC_ENUM 0x400 列挙型修飾子

上記のクラスのアクセス修飾子です、それはまた、プロパティ、メソッドに似ていますが、比較的簡単ですが、ここでは継続しないことです。

タイプテーブル

JAVAの基本的な種類、配列、及び対象において、前記テーブルのバイトコード分化型は次のように

タイプ バイトコード表現 定義
バイト B バイト
ブーリアン とともに BOOL
CHAR C 文字
ショート S 短整数
int型 整数
浮く F フロート
長いです J 長整数
ダブル D フロート
空隙 V ヌル戻り値
カテゴリ Ljava / LANG /オブジェクト。 オブジェクトの種類
配列 [] [

カテゴリは、Lとミドルクラスパスを開始し、そして最後で終了あり;上記アレイは、単一のアレイであり、そのような[]バイトコードが[] [] WordをINT、[IであるINTのような他のタイプと一緒に使用することが部分コードは[Iです。

分析方法

アクセス修飾子は、上述されており、次のようにJavaバイトコードのタイプを制御、解析方法についての以下の説明、例えばは、上記の方法を取ります。

public java.lang.String getName();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field name:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 9: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/sec/resourceparse/Person;
复制代码
  • 記述:記述された方法は、パラメータと戻り型、フォーマット方法記載されている:(パラメータタイプ)戻り型を、基準値とバック文字列のない表現が存在しません
  • フラグ:公共としてここに示したアクセス修飾子の方法、
  • コード:スタックに記載されている具体的な方法
  • スタック:スタック割り当ての最大深さを
  • メソッド内のローカル変数の数:地元の人々
  • args_size:パラメータのメソッド番号
  • LineNumberTable:情報の行のメソッド(心配しないで、見ていません)
  • LocalVariableTable:ローカル変数テーブル

ここでは簡単に、クラスメソッド少なくとも1つのパラメータを説明し、パラメータは、クラスオブジェクト自体、このキーワードの同等であり、添字はゼロです。

バイトコード命令

すでに関連の基礎バイトコードをカバーしますが、バイトコード命令に関連する内容を詳しく説明していなかった、バイトコード命令の内容に焦点を当てるこのセクションでは、バイトコード命令は、次のカテゴリに分類されます。

  • 負荷型命令を格納する
    などスタック操作パラメータまたはローカル変数にオペランドスタックに格納されたデータ、負荷を含む一連の命令、ストア命令とプッシュにロードされます
  • オブジェクト操作指示
    例えばウェルgetStatic putFieldのgetField等とputStaticなどの操作を、取得オブジェクトのプロパティから新たなオブジェクトを生成するなど、ターゲット命令
  • DUPと、このような打ち上げにプッシュとポップ命令のようなポップスタックの管理命令
  • 操作指示
    操作指示データ等主に加算、減算命令である、ここで、スタック操作のみで実行され
  • 命令制御をジャンプ
    条件決意命令など、など後藤ifelse
  • メソッド呼び出し及びリターン命令
    一連の命令を呼び出す命令実行の方法を呼び出す命令系列を返すなどは、リターン命令の戻りシリーズであります

操作スタックプロセス

上記実質的に実用的な方法は、動作の流れを説明するためのバイトコードの内容のすべてを完了しました。次の点を覚えておく:
JAVAスタックベースの実行の方法が行われ、コールスタックの後にメソッド呼び出し命令は、メソッドが値を返すかどうか、値が返されますプッシュされます

まず、単純な分析:

public java.lang.String getName();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field name:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 9: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/sec/resourceparse/Person;
复制代码

1.aload_0:0は最初のパラメータで、プッシュ型パラメータがオブジェクトである(フロント分析は、このあります)

2.getfield:属性名スタックオブジェクトの現在のトップから取得し、スタックにそれを押します

3.areturn:スタックの現在のトップString型の値であるので、使用areturnの使用に戻ります

もう少し複雑列子再導入:

public class Manager {

    public static void main(String [] args) {
        String resPath = "/Users/Desktop/resources.arsc";
        FileInputStream ins = null;
        ByteArrayOutputStream ous = null;
        try {
            ins = new FileInputStream(new File(resPath));
            ous = new ByteArrayOutputStream();
            int length = -1;
            byte data[] = new byte[4 * 1024];
            while ((length = ins.read(data)) != -1) {
                ous.write(data, 0, length);
            }
            byte[] resData = ous.toByteArray();
            ParseUtils.parseRes(resData);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
复制代码

次のようにバイトコードに対応します:

 public com.sec.resourceparse.Manager();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/sec/resourceparse/Manager;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=5, locals=7, args_size=1
         0: ldc           #2                  // String /Users/Desktop/resources.arsc
         2: astore_1
         3: aconst_null
         4: astore_2
         5: aconst_null
         6: astore_3
         7: new           #3                  // class java/io/FileInputStream
        10: dup
        11: new           #4                  // class java/io/File
        14: dup
        15: aload_1
        16: invokespecial #5                  // Method java/io/File."<init>":(Ljava/lang/String;)V
        19: invokespecial #6                  // Method java/io/FileInputStream."<init>":(Ljava/io/File;)V
        22: astore_2
        23: new           #7                  // class java/io/ByteArrayOutputStream
        26: dup
        27: invokespecial #8                  // Method java/io/ByteArrayOutputStream."<init>":()V
        30: astore_3
        31: iconst_m1
        32: istore        4
        34: sipush        4096
        37: newarray       byte
        39: astore        5
        41: aload_2
        42: aload         5
        44: invokevirtual #9                  // Method java/io/FileInputStream.read:([B)I
        47: dup
        48: istore        4
        50: iconst_m1
        51: if_icmpeq     66
        54: aload_3
        55: aload         5
        57: iconst_0
        58: iload         4
        60: invokevirtual #10                 // Method java/io/ByteArrayOutputStream.write:([BII)V
        63: goto          41
        66: aload_3
        67: invokevirtual #11                 // Method java/io/ByteArrayOutputStream.toByteArray:()[B
        70: astore        6
        72: aload         6
        74: invokestatic  #12                 // Method com/sec/resourceparse/ParseUtils.parseRes:([B)V
        77: goto          87
        80: astore        4
        82: aload         4
        84: invokevirtual #14                 // Method java/lang/Exception.printStackTrace:()V
        87: return

复制代码

Managerは、静的なmainメソッドを宣言していますが、バイトコードのinitは、実際には、引数なしでデフォルトコンストラクタは、このメソッドのバイトコードを見ての方法があります。

public com.sec.resourceparse.Manager();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/sec/resourceparse/Manager;
复制代码

1.aload_0:このオブジェクトは、スタックにプッシュする
2.invokespecial:特別な方法は、initのスタックオブジェクトメソッド、戻り値のinit Vの種類を呼び出すので、呼び出しスタックが空になった後
3.return:NOスタックがないので値が、それには直接実行リターン命令

再フォーカス別の方法を見て:

public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=5, locals=7, args_size=1
         0: ldc           #2                  // String /Users/Desktop/resources.arsc
         2: astore_1
         3: aconst_null
         4: astore_2
         5: aconst_null
         6: astore_3
         7: new           #3                  // class java/io/FileInputStream
        10: dup
        11: new           #4                  // class java/io/File
        14: dup
        15: aload_1
        16: invokespecial #5                  // Method java/io/File."<init>":(Ljava/lang/String;)V
        19: invokespecial #6                  // Method java/io/FileInputStream."<init>":(Ljava/io/File;)V
        22: astore_2
        23: new           #7                  // class java/io/ByteArrayOutputStream
        26: dup
        27: invokespecial #8                  // Method java/io/ByteArrayOutputStream."<init>":()V
        30: astore_3
        31: iconst_m1
        32: istore        4
        34: sipush        4096
        37: newarray       byte
        39: astore        5
        41: aload_2
        42: aload         5
        44: invokevirtual #9                  // Method java/io/FileInputStream.read:([B)I
        47: dup
        48: istore        4
        50: iconst_m1
        51: if_icmpeq     66
        54: aload_3
        55: aload         5
        57: iconst_0
        58: iload         4
        60: invokevirtual #10                 // Method java/io/ByteArrayOutputStream.write:([BII)V
        63: goto          41
        66: aload_3
        67: invokevirtual #11                 // Method java/io/ByteArrayOutputStream.toByteArray:()[B
        70: astore        6
        72: aload         6
        74: invokestatic  #12                 // Method com/sec/resourceparse/ParseUtils.parseRes:([B)V
        77: goto          87
        80: astore        4
        82: aload         4
        84: invokevirtual #14                 // Method java/lang/Exception.printStackTrace:()V
        87: return
复制代码

メソッドの説明:

  1. ([Ljava /ラング/文字列;)V:Stringパラメータは、一次元アレイ、ノーリターン値であります
  2. フラグ:静的および公共のためのアクセス修飾子

スタック操作の説明:
0:オブジェクトプッシュString型、値: "/ユーザ/デスクトップ/ resources.arsc "

2-6:

  1. 先頭の要素1とローカル変数の存在をポップ
  2. スタックにはnull
  3. 先頭の要素はnull、およびローカル変数2の存在をポップ
  4. スタックにはnull
  5. 先頭の要素はnullをポップし、ローカル変数があります。3

次のように上記の動作が完了した後、スタックとローカル変数の方法があります。

7-30:

  1. 新しいJava / IO / FileInputStreamをオブジェクト、およびスタックにプッシュ
  2. DUP:上記オブジェクトが生成され、スタックにプッシュされ、オブジェクトの二つの電流のスタックがあるのFileInputStream
  3. 新しいJava / IO / Fileオブジェクト、およびスタックにプッシュ
  4. DUP:ファイルオブジェクトは、次いで、上記で生成されたスタックにプッシュなり、二つの電流のスタックは、ファイルオブジェクトが存在します
  5. aload_1:スタックに1つのローカル変数、スタック文字列へ、すなわち値
  6. invokespecial:initメソッドを呼び出して、ファイル、パラメータは文字列、ノーリターン値であります
    説明:オブジェクトを作成した後、二回スタック押される理由5-6は、そのコンストラクタプロセスを呼び出して、Fileオブジェクトの外に作成することが把握すべきです
  7. invokespecial:FileInputStreamのオブジェクトのメソッド呼び出し、パラメータは、ファイル、ノーリターン値です
  8. astore_2:トップ要素2はローカル変数に格納されます
  9. 新しいJava / IO / ByteArrayOutputStreamオブジェクト、およびスタックにプッシュ
  10. DUP:上記オブジェクトが生成され、スタックにプッシュされ、二つの電流のスタックオブジェクトが存在するByteArrayOutputStream
  11. invokespecial:ByteArrayOutputStreamのinitメソッドを呼び出します
  12. ローカル変数3に格納上部要素

31-42

  1. iconst_m1:-1スタックにプッシュされます
  2. istore 4:ローカル変数は4に保存され、スタックをポップ
  3. sipush 4096:スタックに4096 int型数
  4. NEWARRAY:スタックのうち、アレイを作成し、スタックにプッシュ
  5. astore 5:ポップアップ要素をスタックとローカル変数5に
  6. aload_2:スタック2の上にローカル変数
  7. aload 5:スタック5の上にローカル変数
  8. INVOKEVIRTUAL:FileInputStreamのメソッドは、パラメータのバイト配列、int型の戻り値を読んで実行します
  9. DUP:トップ要素をコピーし、スタックにプッシュ
  10. istore 4:先頭の要素をポップし、ローカル変数に保存されている4
  11. iconst_m1:-1はスタックにプッシュされ、その後、
  12. 66 if_icmpeq:スタック2 int型の数の比較は、ライン66に等しい、等しい、直接ジャンプしている次のロジックを実装するための責任があります

上記の分析の基本的なロジックので、この方法が比較的長い、分析を下に継続していないが、同様の工程であります

キープロセススタック操作:すべての操作は、クラス・パラメータを使用して、そのようなメソッド呼び出しとして、スタックプッシュとロジックを伴うおよび方法は、圧力の戻り値を値を返す場合スタックは、ポップされスタック。

概要

バイトコードの知識は、バイトコードJVMの知識を理解することが非常に重要である明確運用メカニズムだけでなく、直接操作するバイトコードAOPベースの背面を理解することができます。

参考:
segmentfault.com/a/119000000 ... my.oschina.net/ta8210/blog ...

おすすめ

転載: juejin.im/post/5d5fadf56fb9a06b19734262