天天看點

學弟學妹們,如果你想吃透 Java位元組碼的話,看這篇就好了!(超級硬核,建議收藏2)

睜大眼睛瞧過去,感覺内容挺多的。同學們不要着急,我們來一行一行分析。

第 1 行:

Classfile /Users/maweiqing/Documents/GitHub/TechSisterLearnJava/codes/TechSister/target/classes/com/itwanger/jvm/Main.class

位元組碼檔案的位置。

第 2 行:

Last modified 2021年4月15日; size 385 bytes

位元組碼檔案的修改日期、檔案大小。

第 3 行:

SHA-256 checksum 6688843e4f70ae8d83040dc7c8e2dd3694bf10ba7c518a6ea9b88b318a8967c

位元組碼檔案的 SHA-256 值。

第 4 行:

Compiled from "Main.java"

說明該位元組碼檔案編譯自 Main.java 源檔案。

第 5 行:

public class com.itwanger.jvm.Main

位元組碼檔案的類全名。

第 6 行 minor version: 0,次版本号。

第 7 行 major version: 55,主版本号。

第 8 行:

flags: (0x0021) ACC_PUBLIC, ACC_SUPER

類通路标記,一共有 8 種。

表明目前類是 ACC_PUBLIC | ACC_SUPER。位運算符 | 的意思是如果相對應位是 0,則結果為 0,否則為 1,是以 0x0001 | 0x0020 的結果是 0x0021(需要轉成二進制進行運算)。

第 9 行:

this_class: #3                          // com/itwanger/jvm/Main

目前類的索引,指向常量池中下标為 3 的常量,可以看得出目前類是 Main 類。

第 10 行:

super_class: #4                         // java/lang/Object

父類的索引,指向常量池中下标為 6 的常量,可以看得出目前類的父類是 Object 類。

第 11 行:

interfaces: 0, fields: 1, methods: 2, attributes: 1

目前類有 0 個接口,1 個字段(age),2 個方法(write()方法和預設的預設構造方法),1 個屬性(該類僅有的一個屬性是 SourceFIle,包含了源碼檔案的資訊)。

03、常量池

接下來是 Constant pool,也就是位元組碼檔案最重要的常量池部分。可以把常量池了解為位元組碼檔案中的資源倉庫,主要存放兩大類資訊。

1)字面量(Literal),有點類似 Java 中的常量概念,比如文本字元串,final 常量等。

2)符号引用(Symbolic References),屬于編譯原理方面的概念,包括 3 種:

類和接口的全限定名(Fully Qualified Name)

字段的名稱和描述符(Descriptor)

方法的名稱和描述符

Java 虛拟機是在加載位元組碼檔案的時候才進行的動态連結,也就是說,字段和方法的符号引用隻有經過運作期轉換後才能獲得真正的記憶體位址。當 Java 虛拟機運作時,需要從常量池擷取對應的符号引用,然後在類建立或者運作時解析并翻譯到具體的記憶體位址上。

目前位元組碼檔案中一共有 21 個常量,它們之間是有連結的,逐個分析會比較亂,我們采用順藤摸瓜的方式,從上依次往下看,那些被連結的常量我們就點到為止。

注:

# 号後面跟的是索引,索引沒有從 0 開始而是從 1 開始,是因為設計者考慮到,“如果要表達不引用任何一個常量的含義時,可以将索引值設為 0 來表示”(《深入了解 Java 虛拟機》描述的)。

= 号後面跟的是常量的類型,沒有包含字首 CONSTANT_ 和字尾 _info。

全文中提到的索引等同于下标,為了靈活描述,沒有做統一。

第 1 個常量:

#1 = Methodref          #4.#18         // java/lang/Object."<init>":()V

類型為 Methodref,表明是用來定義方法的,指向常量池中下标為 4 和 18 的常量。

第 4 個常量:

#4 = Class              #21            // java/lang/Object

類型為 Class,表明是用來定義類(或者接口)的,指向常量池中下标為 21 的常量。

第 21 個常量:

#21 = Utf8               java/lang/Object

類型為 Utf8,UTF-8 編碼的字元串,值為 java/lang/Object。

第 18 個常量:

#18 = NameAndType        #7:#8          // "<init>":()V

類型為 NameAndType,表明是字段或者方法的部分符号引用,指向常量池中下标為 7 和 8 的常量。

第 7 個常量:

#7 = Utf8               <init>

類型為 Utf8,UTF-8 編碼的字元串,值為 <init>,表明為構造方法。

第 8 個常量:

#8 = Utf8               ()V

類型為 Utf8,UTF-8 編碼的字元串,值為 ()V,表明方法的傳回值為 void。

到此為止,第 1 個常量算是摸完了。組合起來的意思就是,Main 類使用的是預設的構造方法,來源于 Object 類。

第 2 個常量:

#2 = Fieldref           #3.#19         // com/itwanger/jvm/Main.age:I

類型為 Fieldref,表明是用來定義字段的,指向常量池中下标為 3 和 19 的常量。

第 3 個常量:

#3 = Class              #20            // com/itwanger/jvm/Main

類型為 Class,表明是用來定義類(或者接口)的,指向常量池中下标為 20 的常量。

第 19 個常量:

#19 = NameAndType        #5:#6          // age:I

類型為 NameAndType,表明是字段或者方法的部分符号引用,指向常量池中下标為 5 和 6 的常量。

第 5 個常量:

#5 = Utf8               age

類型為 Utf8,UTF-8 編碼的字元串,值為 age,表明字段名為 age。

第 6 個常量:

#6 = Utf8               I

類型為 Utf8,UTF-8 編碼的字元串,值為 I,表明字段的類型為 int。

關于字段類型的描述符映射表如下圖所示。

學弟學妹們,如果你想吃透 Java位元組碼的話,看這篇就好了!(超級硬核,建議收藏2)