詳解 JVM 位元組碼(3)

jvm
版本資訊
上一次我們分析前 4 個位元組為 魔數 ,繼續向下數 4 位元組為 版本號 資訊(前兩個位元組表示此版本號,後兩個位元組表示主版本號)
00 00 00 36 。36 是 16 進位制進行換算 3 * 16 + 6 = 54 這裡用的 java 的版本號 10.0.1 版本。
minor version: 0 major version: 54
常量池
接下來就是常量池,常量池大小是不固定的。
常量池定義與作用
Java 類中定義的很多資訊都是由常量池來維護和描述的。而且位元組碼其他部分都會引用常量池的內容。所以將常量池看成 Java 類資源倉庫。
主要儲存兩類常量
- 字面量 :文字字串,java 中宣告為 final 的常量值
- 符號引用 :類和介面的全域性(包名)限定名,欄位和方法的名稱和描述符
常量池的結構
- 常量池數量:佔據 2 位元組 00 22
- 常量池陣列:常量池陣列中不同元素的型別、結構都是不同的。每一種元素第一個資料都是 u1 型別,是標誌位,佔一個位元組。JVM 在解析常量池時,會根據這個 u1 型別獲取元素具體資訊。
這裡常量池數量 22 16 * 2 + 2 = 34
#1 = Methodref#6.#28// java/lang/Object."<init>":()V #2 = String#29// basic #3 = Fieldref#5.#30// com/zidea/Tut.title:Ljava/lang/String; #4 = Fieldref#5.#31// com/zidea/Tut.courses:I #5 = Class#32// com/zidea/Tut #6 = Class#33// java/lang/Object #7 = Utf8title #8 = Utf8Ljava/lang/String; #9 = Utf8courses #10 = Utf8I #11 = Utf8<init> #12 = Utf8()V #13 = Utf8Code #14 = Utf8LineNumberTable #15 = Utf8LocalVariableTable #16 = Utf8this #17 = Utf8Lcom/zidea/Tut; #18 = Utf8getTitle #19 = Utf8()Ljava/lang/String; #20 = Utf8setTitle #21 = Utf8(Ljava/lang/String;)V #22 = Utf8getCourses #23 = Utf8()I #24 = Utf8setCourses #25 = Utf8(I)V #26 = Utf8SourceFile #27 = Utf8Tut.java #28 = NameAndType#11:#12// "<init>":()V #29 = Utf8basic #30 = NameAndType#7:#8// title:Ljava/lang/String; #31 = NameAndType#9:#10// courses:I #32 = Utf8com/zidea/Tut #33 = Utf8java/lang/Object
為什麼是 33 而不是 34,常量池陣列個數等於常量池數 - 1 ,其中 0 暫時不使用,索引為 0 是 JVM 保留常量,這個常量不位於常量池,這個常量對應 null 。

常量池11種資料型別的結構表
這個表不用記,學會查表就行
tag - U1 表示常量型別
1. java/lang/Object."<init>":()V
0A: 10
對應查表 U1 為 10 是 CONSTANT_Methodref_Info
00 06 (6)index: 指向宣告方法的類描述符CONSTANT_Class_Info的索引項
00 1C(對應十進位制 28) index: 指向名稱及型別描述符 CONSTANT_NameAndType_Info的索引項
#1 = Methodref #6.#28 // java/lang/Object."<init>":()V
-
#6 = Class #33 // java/lang/Object
-
#33 = Utf8 java/lang/Object
#28 = NameAndType #11:#12 // "<init>":()V
-
#11 = Utf8 <init>
-
#12 = Utf8 ()V
這裡出現許多看似簡寫的符號,看一看他們都代表什麼含義?
在 JVM 規範中,每個變數/欄位都有描述資訊,用於描述欄位的資料型別,方法的引數列表(數量、型別與順序)與返回值。
描述規則基本資料型別和代表無返回值的 void 型別都用一個大寫字元來表示,物件型別則使用字元L加物件全域性名稱來表示。
-
B : byte
-
C : char
-
D : double
-
F : float
-
I : Int
-
J : Long
-
S : short
-
Z: boolean
-
V: void
-
L : 物件 Ljava/lang/String
陣列型別來說,每一個維度使用一個前置[來表示,例如int[] 被記錄為[I,String[][] 被記錄為[[Ljava/lang/String。
描述符描述方法時,按照先引數列表,後返回值的順序來描述。引數列表按照引數的嚴格順序存放在一組()之內。
public void setTitle(String title)
編譯為位元組碼 (Ljava/lang/String;)V
2. basic
08 識別符號對應 CONSTANT_String_Info
- 00 1D(29) index 指向字串字面量索引
- #29 = Utf8 basic
在 Tut 類將 title 欄位賦值為 basic 這個字串。
3. com/zidea/Tut.title:Ljava/lang/String;
09 識別符號對應 CONSTANT_Fieldref_Info
- 00 05 (5): 指向宣告欄位的類或者介面描述符 CONSTANT_Class_Info 的索引
- 00 1E (30): 指向欄位描述符 CONSTANT_NameAndType_Info 的索引值
指向類描述符為 com/zidea/Tut 為欄位所屬的類
欄位名稱和型別 #30 = NameAndType #7:#8
#7: title 表示欄位名稱
#8: Ljava/lang/String 欄位型別為字串
表示 Tut 類的 String 型別變數 title
4. com/zidea/Tut.courses:I
09 識別符號 CONSTANT_Fieldref_Info
00 05 (5)重複就不多解釋了,代表類
00 1F (31)
31 = NameAndType #9:#10 // courses:I
- course 為變數
- I 為型別 int
以上就結束兩個變數的宣告
5. com/zidea/Tut
07 對應識別符號為 CONSTANT_Class_Info
00 20 (32) 指向全侷限定名常量項的索引
32 = Utf8 com/zidea/Tut
這個類全限定名稱
6. java/lang/Object
又是 07 大家自己嘗試分析
7. title
01 識別符號為 CONSTANT_utf8_Info
- 00 05 length 表示五個位元組,title 就是由五個字元組成的。
- 74 69 74 6C 65 分為 title 五個字元的ASCII碼
8. Ljava/lang/String;
- 00 12 (18) 向後數 18 位元組
- 4C 6A 61 .!...title...Lja
00000030: 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B(對應字元 Ljava/lang/String;)
9. courses
10. I
...
#33 = Utf8 java/lang/Object
我相信大家已經了回了怎麼進行分析,大家可以自己完成。
這兩號表示當前的位元組碼檔案是由那個檔案編譯出來的。
26 = Utf8 SourceFile
27 = Utf8 Tut.java
在位元組碼使用 /
代替 .
分隔符。