序文
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
复制代码
メソッドの説明:
- ([Ljava /ラング/文字列;)V:Stringパラメータは、一次元アレイ、ノーリターン値であります
- フラグ:静的および公共のためのアクセス修飾子
スタック操作の説明:
0:オブジェクトプッシュString型、値: "/ユーザ/デスクトップ/ resources.arsc "
- 先頭の要素1とローカル変数の存在をポップ
- スタックにはnull
- 先頭の要素はnull、およびローカル変数2の存在をポップ
- スタックにはnull
- 先頭の要素はnullをポップし、ローカル変数があります。3
次のように上記の動作が完了した後、スタックとローカル変数の方法があります。
7-30:
- 新しいJava / IO / FileInputStreamをオブジェクト、およびスタックにプッシュ
- DUP:上記オブジェクトが生成され、スタックにプッシュされ、オブジェクトの二つの電流のスタックがあるのFileInputStream
- 新しいJava / IO / Fileオブジェクト、およびスタックにプッシュ
- DUP:ファイルオブジェクトは、次いで、上記で生成されたスタックにプッシュなり、二つの電流のスタックは、ファイルオブジェクトが存在します
- aload_1:スタックに1つのローカル変数、スタック文字列へ、すなわち値
- invokespecial:initメソッドを呼び出して、ファイル、パラメータは文字列、ノーリターン値であります 説明:オブジェクトを作成した後、二回スタック押される理由5-6は、そのコンストラクタプロセスを呼び出して、Fileオブジェクトの外に作成することが把握すべきです
- invokespecial:FileInputStreamのオブジェクトのメソッド呼び出し、パラメータは、ファイル、ノーリターン値です
- astore_2:トップ要素2はローカル変数に格納されます
- 新しいJava / IO / ByteArrayOutputStreamオブジェクト、およびスタックにプッシュ
- DUP:上記オブジェクトが生成され、スタックにプッシュされ、二つの電流のスタックオブジェクトが存在するByteArrayOutputStream
- invokespecial:ByteArrayOutputStreamのinitメソッドを呼び出します
- ローカル変数3に格納上部要素
31-42
- iconst_m1:-1スタックにプッシュされます
- istore 4:ローカル変数は4に保存され、スタックをポップ
- sipush 4096:スタックに4096 int型数
- NEWARRAY:スタックのうち、アレイを作成し、スタックにプッシュ
- astore 5:ポップアップ要素をスタックとローカル変数5に
- aload_2:スタック2の上にローカル変数
- aload 5:スタック5の上にローカル変数
- INVOKEVIRTUAL:FileInputStreamのメソッドは、パラメータのバイト配列、int型の戻り値を読んで実行します
- DUP:トップ要素をコピーし、スタックにプッシュ
- istore 4:先頭の要素をポップし、ローカル変数に保存されている4
- iconst_m1:-1はスタックにプッシュされ、その後、
- 66 if_icmpeq:スタック2 int型の数の比較は、ライン66に等しい、等しい、直接ジャンプしている次のロジックを実装するための責任があります
上記の分析の基本的なロジックので、この方法が比較的長い、分析を下に継続していないが、同様の工程であります
キープロセススタック操作:すべての操作は、クラス・パラメータを使用して、そのようなメソッド呼び出しとして、スタックプッシュとロジックを伴うおよび方法は、圧力の戻り値を値を返す場合スタックは、ポップされスタック。
概要
バイトコードの知識は、バイトコードJVMの知識を理解することが非常に重要である明確運用メカニズムだけでなく、直接操作するバイトコードAOPベースの背面を理解することができます。
参考:
segmentfault.com/a/119000000 ... my.oschina.net/ta8210/blog ...