詳解 JVM 位元組碼(5)

jvm
今天進入最關鍵也是最重要部分方法表,也是有一定難度。
定義方法訪問標記
00 01 :表示一個 public 方法,這是一個構造方法。
00 0B : 對應常量池 # 11 <init>
00 0C : 對應常量池 #12 ()V
00 01 : 說明這個方法本身只有一個屬性,每一個方法表都有 code 屬性儲存這個方法的結構
00 0D : #13 = Utf8 Code
00 00 00 43 (67): 位元組長度

code 資訊
00 02 :max_stack
00 01 : max_local
00 00 00 11: (17)code_length 表示該方法所包含位元組碼的位元組數以及具體的指令碼
我們可以根據 code_length 向後數 17 位元組,這是函式 code 執行指令
2A B7 00 01 2A 12 02 B5 00 03 2A 10 0A B5 00 04 B1

code 資訊
此圖與上面位元組碼相對應。圖中出現的為助記符,助記符和16進位制對應
-
2A (aload_0)
可以通過在 jclasslib 面板點選助記符
aload_o
跳轉到 JVM 官方 JVM 規範的中助記符對應章節。aload 助記符
-
B7 (invokespecial)
invokespecial 助記符
表示呼叫父類的構造方法,而且還有引數為其後面的兩個位元組,
00 01 :#1 = Methodref #6.#28 // java/lang/Object."<init>":()V
表示父類的構造方法呼叫 Object 的 init 方法
-
0A
-
12(ldc)
ldc 助記符
從常量池獲取元素
-
02 #2 = String #29 // basic
-
B5 (putfiled)
putfield
為物件設定屬性,屬性為
00 03 #3 = Fieldref #5.#30 // com/zidea/Tut.title:Ljava/lang/String;
上面是從常量池讀取 basic 字面量賦值給物件屬性 title。
-
10(bipush)
bipush
具體賦值為其後位元組 0A(10)
將 10 放置到棧頂然後準備給 courses 屬性進行賦值,內容重複大家可以自己感受一下
-
B1(return)
return
表示方法執行完畢,返回為 void。
隨後兩個位元組使用表示異常表資訊的數量,如果為 0 表示沒有異常資訊表
表示異常表資訊,異常表數量為 0 沒有異常資訊。
表示存在兩個屬性,隨後
00 0E
這裡的 00 0E(14) #14 = Utf8 LineNumberTable 對應常量池內容,LineNumberTable 用途是將位元組碼和實際程式碼行號進行對應便於除錯查詢問題
00 00 00 0E
00 03 00 00 00 03 00 04 00 04 00 0A 00 05
00 03 表示有三個對應關係
-
00 00 00 03 :0 對應 3
-
00 04 00 04
-
00 0A 00 05 :10 對應 5 行程式碼
LineNumberTable
[圖片上傳失敗...(image-950363-1556845759146)]
-
00 0F (15) #15 = Utf8 LocalVariableTable
表示第二個屬性名字為 15 對應表示區域性變量表
00 00 00 0C 表示連續 12 位元組表示區域性變數
-
00 01 表示構造方法裡區域性變數的個數
-
00 00 和 00 11 表示區域性變數開始和結束的位置
-
00 10 (16)表示區域性變數在常量池位置 #16 = Utf8 this
-
00 11 (17)對區域性變數的描述 #17 = Utf8 Lcom/zidea/Tut;
-
00 00 可以跳過去
我們知道 java 中每一個方法都是訪問到
this
其實在 JVM 編譯時,this 是作為第一個引數傳入到方法才能被使用。我們第一個構造方法的位元組碼分析到此就結束,可以開始下一個方法也就是
getTitle
,相同內容我就不多贅述了,大家可以自動手查一查。 -
00 01 (access_flag) ACC_PUBLIC)
00 12 : #18 = Utf8 getTitle(方法名)
00 13 : #19 = Utf8 ()Ljava/lang/String;(描述符)
00 01 :表示方法屬性
00 0D :對應 code
00 00 00 2F(47)位元組表示 code 的資訊
00 01 :
00 01 :區域性變量表為 this。
00 00 00 05 :表示該方法位元組碼所佔長度,可以向後數 5 個位元組。
-
2A aload_0
-
B4 (getfield) 從成員變數獲取值 引數為 00 03 表示指向常量池 3 元素 #3 = Fieldref #5.#30 // com/zidea/Tut.title:Ljava/lang/String;
-
B0(Return reference from method)表示返回一個引用,因為字串 ”basic“ 是一個引用物件所以這裡返回字串的引用。
-
00 00 :表示沒有異常表
-
00 02 :表示兩個屬性
-
00 0E : 第一個屬性為 LineNumberTable
回顧一下 LineNumberTable 的屬性有哪些
-
attribute_name_index u2
-
attribute_length u4
-
line_number_table_length
- start_pc
- line_number
-
00 00 00 06 (屬性長度為 6 )
表示偏移量為 0 對應行號 8 ,行號 8 內容為 return title;
。
- 00 0F 區域性變量表
- 00 00 00 0C 表示區域性變數的長度 12 數 12 位元組
表示有一個區域性變數 this 。
- 00 00
這樣就結束了getTitle
方法,我們可以開始第三個方法public void setTitle(String title)
- 00 01 pubic
- 00 14 : #20 = Utf8 setTitle
- 00 15 : (Ljava/lang/String;)V 返回值為 void 接收一個 String 物件作為引數
- 00 01 : 方法的屬性
- 00 0D : 方法屬性為 Code
- 00 00 00 3E : 62 位元組代表 code 對應位元組碼
- 00 02 : 最大操作棧數
- 00 02 :區域性變數個數
- 00 00 00 06:方法執行位元組碼
2A 2B B5 00 03 B1
-
2A(aload_0)
-
2B(aloda_1)
-
B5(putfield) 引數 03 賦值給 title
-
B1(return)
-
00 00
-
00 02
-
00 0E 行號表
-
00 00 00 0A 向後數 10 個位元組
00 02 00 00 00 0C 00 05 00 0D
有兩個對應關係
- 00 0F :區域性變量表
- 00 00 00 16:表示有 22 個位元組表示區域性變量表的資訊
- 00 02 表示有兩個區域性變數
00 01 00 1A 00 00 00 02 00 1B
隨後位元組碼可以按上面規則進行分析。