Android學習筆記 手動執(zhí)行Java字節(jié)碼
@[Android, 字節(jié)碼]
背景
這一次是玩安卓知識星球的第二期作業(yè)(分析每一行字節(jié)碼執(zhí)行過程中的本地變量數(shù)組和操作數(shù)棧的狀態(tài))岂丘,完成第一期作業(yè)的過程中需要需要操作字節(jié)碼,但借助Javassist的幫助也沒有去學習字節(jié)碼的相關知識,現(xiàn)在不得不好好的看看Java的字節(jié)碼究竟是怎樣執(zhí)行的渤滞。
JVM的內存模型
首先復習一下JVM的內存模型 ,JVM的內存模型分為幾個區(qū)域,如下圖:
- 程序計數(shù)器:這是線程私有的數(shù)據(jù)區(qū)厨相,用來表示當前執(zhí)行的是哪一行的指令
- 虛擬機棧:一個棧結構,存儲的元素被稱為棧幀鸥鹉,每個棧幀包含了一個方法的數(shù)據(jù)和部分過程結果的區(qū)域蛮穿,同時包含動態(tài)連接的信息和方法返回值
- 局部變量表:用來保存方法局部變量的數(shù)組結構
- 操作數(shù)棧:JVM是基于棧結構,操作數(shù)棧就是具體用輔助執(zhí)行指令的棧毁渗,棧深度在編譯期就寫入到class文件中
- 動態(tài)鏈接:作用是將對方法符號引用轉換為直接引用
- 返回地址:方法執(zhí)行完成后返回的位置践磅,即調用自己的位置
- 本地方法棧:跟虛擬機棧類似的為本地方法服務的棧結構
- 方法區(qū):線程共享的區(qū)域,存儲運行時常量池灸异,方法的字節(jié)碼數(shù)據(jù)府适,類的字段等
- 堆:也是線程共享的區(qū)域,存儲的是Java動態(tài)生成的對象數(shù)據(jù)肺樟,垃圾回收器管理的也是這的區(qū)域
class文件
對JVM內存模型有了個簡單的回顧檐春,我們繼續(xù)看看class文件的結構是怎么樣的。
- 魔數(shù)和版本號都是對文件本身的描述
- 常量池:存儲的是字面量和符號引用么伯,除了對一些基本類型的操作疟暖,像是類名、方法名田柔、常量等都在這里有定義
- 訪問標記:標記class是表示一個類還是一個接口俐巴,是否公開,是否是abstract等信息
- 類索引:引用常量池中的類的全限定類名
- 父類索引:引用常量池中的父類的全限定類名
- 接口索引:接口數(shù)量和引用常量池中的接口的全限定接口名
- 字段表集合:定義類所有字段的各種屬性
- 方法:包含每個方法的各種屬性硬爆,其中就包括上面說到的操作數(shù)棧的深度
- 屬性:定義了class文件的屬性欣舵,比如源代碼文件名
手動執(zhí)行字節(jié)碼指令
接下來結合代碼,我們來手動執(zhí)行字節(jié)碼指令:
public class Hello {
public static void main(String[] args) {
int a = 1;
int b = 10;
String c = "abc";
System.out.println(a + b + c + get3());
}
private static int get3() {
int result = 0;
for (int i = 0; i < 3; i++) {
result ++;
}
return result;
}
}
// 生成的字節(jié)碼指令
// main()
0 iconst_1
1 istore_1
2 bipush 10
4 istore_2
5 ldc #2 <abc>
7 astore_3
8 getstatic #3 <java/lang/System.out>
11 new #4 <java/lang/StringBuilder>
14 dup
15 invokespecial #5 <java/lang/StringBuilder.<init>>
18 iload_1
19 iload_2
20 iadd
21 invokevirtual #6 <java/lang/StringBuilder.append>
24 aload_3
25 invokevirtual #7 <java/lang/StringBuilder.append>
28 invokestatic #8 <com/zhixiao/readclass/Hello.get3>
31 invokevirtual #6 <java/lang/StringBuilder.append>
34 invokevirtual #9 <java/lang/StringBuilder.toString>
37 invokevirtual #10 <java/io/PrintStream.println>
40 return
// get3()
0 iconst_0
1 istore_0
2 iconst_0
3 istore_1
4 iload_1
5 iconst_3
6 if_icmpge 18 (+12)
9 iinc 0 by 1
12 iinc 1 by 1
15 goto 4 (-11)
18 iload_0
19 ireturn
代碼很簡單缀磕,我們先來看以下各部分在虛擬機中的存儲情況
代碼加載到方法區(qū)缘圈,正在被執(zhí)行的方法作為棧幀被初始化到虛擬機棧,執(zhí)行第一條指令虐骑,常量1被壓入棧
new對象時准验,在堆生成該對象,并將對象的地址壓入棧
執(zhí)行方法時廷没,調用方法生成新的棧幀并壓入虛擬機棧,然后將返回值設為方法調用處的下一條指令
退出方法時從返回地址返回垂寥,如果有返回值則將棧頂?shù)闹祲喝胝{用處的操作數(shù)棧
以上只是部分分析過程颠黎,具體可以下載我做的ppt查看全部的過程會更清楚
ppt的地址:
鏈接:https://pan.baidu.com/s/11SDZbSAiiEGtlg5wZi1ztw
提取碼:np8f
總結
將學習的結果寫出來真的花時間花精力另锋,但是我相信通過這樣的方式我會對所學的內容理解更加深刻。現(xiàn)在希望能繼續(xù)堅持下去狭归∝财海可以參考項目地址
參考:
- Java虛擬機規(guī)范_SE8版
- 一文讓你明白Java字節(jié)碼
- Java指令集
- Java編譯原理--運行時棧幀結構