一滩租、分類
1.存儲(chǔ)和加載指令
用于處理 ==操作數(shù)棧、局部變量表利朵、常量池==律想,這三者的調(diào)度。指令包含 load绍弟、store技即、ldc、push
2.對(duì)象操作指令
用于創(chuàng)建對(duì)象和對(duì)象讀寫訪問(wèn)樟遣,字段讀寫訪問(wèn) getfield而叼、putfield, 靜態(tài)字段的讀寫訪問(wèn) getstaic豹悬、putstatic, 對(duì)象創(chuàng)建new 指令葵陵, 還有 instanceof指令
3.操作數(shù)棧管理指令
只用于對(duì)操作數(shù)棧的管理, 包含pop、dup
4. 類型轉(zhuǎn)換和運(yùn)算指令
一般只對(duì)操作數(shù)棧進(jìn)行操作瞻佛,包含 add脱篙、div、l2i
5.控制跳轉(zhuǎn)指令
包含if系列指令和goto指令
6.方法調(diào)用和返回指令
用于方法調(diào)用和返回操作涤久,主要包括invoke系列指令和return系列指令, 當(dāng)調(diào)用invoke指令時(shí)忍弛,會(huì)開辟一個(gè)新的空間(新的棧和局部變量表)執(zhí)行指令, 執(zhí)行return 方法時(shí)响迂,結(jié)束自己的的空間。
二细疚、執(zhí)行
image
從上面的圖中蔗彤, 可以看出基本操作都是圍繞這個(gè)操作棧進(jìn)行的,通過(guò)壓入要出來(lái)的目標(biāo)疯兼,執(zhí)行相應(yīng)的操作之指令后然遏,出棧。當(dāng)調(diào)用 invoke指令時(shí)吧彪,創(chuàng)建一個(gè)新的空間
三待侵、實(shí)例
1. 讀操作
//java
public class MainClient {
private int a = 1;
public static void main(String[] args) {
new MainClient().foo();
}
public int foo() {
return this.a;
}
}
//字節(jié)碼
public int foo();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #2 // Field a:I
4: ireturn
LineNumberTable:
line 11: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/daole/datastructure/MainClient;
}
可以看到如下步驟
- aload_0 對(duì)應(yīng)著本地變量表格,是壓入一個(gè) MainClicent實(shí)例對(duì)象
- getfield #2 姨裸,彈出棧頂?shù)哪繕?biāo)秧倾,獲取對(duì)應(yīng)的字段a怨酝,并把a(bǔ)壓入操作棧。
- ireturn 彈出棧頂目標(biāo)那先,并返回农猬,結(jié)束空間。
2.寫操作
//java
public class MainClient {
private int a = 1;
public static void main(String[] args) {
new MainClient().foo();
}
public void foo() {
this.a = 2;
}
}
//字節(jié)碼
public void foo();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: iconst_2
2: putfield #2 // Field a:I
5: return
LineNumberTable:
line 11: 0
line 12: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/daole/datastructure/MainClient;
可以看到如下步驟
- 同樣是壓入操作對(duì)象
- 壓入一個(gè)常量 2
- putfield 需要兩個(gè)參數(shù)售淡,于是操作數(shù)棧彈出兩個(gè)參數(shù)斤葱, 分別對(duì)應(yīng) aload_0 和 iconst_2 , 獲取a字段后進(jìn)行賦值
- return 結(jié)束空間
3.調(diào)用操作
//java
public class MainClient {
private int a = 0;
public static void main(String[] args) {
new MainClient().foo();
}
public void foo() {
this.a = 2;
foo2();
}
public void foo2() {
this.a = 3;
}
}
//字節(jié)碼
public void foo();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: iconst_2
2: putfield #2 // Field a:I
5: aload_0
6: invokevirtual #6 // Method foo2:()V
9: return
LineNumberTable:
line 11: 0
line 12: 5
line 13: 9
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/daole/datastructure/MainClient;
public void foo2();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: iconst_3
2: putfield #2 // Field a:I
5: return
LineNumberTable:
line 16: 0
line 17: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/daole/datastructure/MainClient;
我們可以看一下有一下幾個(gè)重要的步驟
- foo: 賦值操作結(jié)束后,執(zhí)行了一次aload_0 壓入了 MainClient對(duì)象實(shí)例揖闸,也就是this揍堕。
- foo:彈出this, 執(zhí)行invokevirtual 指令 跳轉(zhuǎn)到新的空間執(zhí)行 foo2方法
- foo2:執(zhí)行賦值操作
- foo2: 調(diào)用return 結(jié)束空間
- foo2: 返回
- foo: 返回