日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

深入理解Java虚拟机(类文件结构)

發布時間:2023/12/31 java 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入理解Java虚拟机(类文件结构) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

歡迎關注微信公眾號:BaronTalk,獲取更多精彩好文!

之前在閱讀 ASM 文檔時,對于已編譯類的結構、方法描述符、訪問標志、ACC_PUBLIC、ACC_PRIVATE、各種字節碼指令等等許多概念聽起來都是云山霧罩、一知半解,原因就在于對類文件結構和類加載機制不夠了解。直到后來細讀了《深入理解 Java 虛擬機》中虛擬機執行子系統的相關內容,才建立了清晰的認知。如果你也和我一樣,不了解類結構和類加載,但是工作中又涉及到字節碼相關內容,相信后面兩篇文章會對你有所幫助。

我們所編寫的每一行代碼,要在機器上運行最終都需要編譯成二進制的機器碼 CPU 才能識別。但是由于虛擬機的存在,屏蔽了操作系統與 CPU 指令集的差異性,類似于 Java 這種建立在虛擬機之上的編程語言通常會編譯成一種中間格式的文件來存儲,比如我們今天要聊的字節碼(ByteCode)文件。

一. 語言無關性

Java 虛擬機的設計者在設計之初就考慮并實現了其它語言在 Java 虛擬機上運行的可能性。所以并不是只有 Java 語言能夠跑在 Java 虛擬機上,時至今日諸如 Kotlin、Groovy、Jython、JRuby 等一大批 JVM 語言都能夠在 Java 虛擬機上運行。它們和 Java 語言一樣都會被編譯器編譯成字節碼文件,然后由虛擬機來執行。所以說類文件(字節碼文件)具有語言無關性。

二. Class 文件結構

Class 文件是一組以 8 位字節為基礎單位的二進制流,各個數據嚴格按照順序緊湊的排列在 Class 文件中,中間無任何分隔符,這使得整個 Class 文件中存儲的內容幾乎全部都是程序運行的必要數據,沒有空隙存在。當遇到需要占用 8 位字節以上空間的數據項時,會按照高位在前的方式分割成若干個 8 位字節進行存儲。

Java 虛擬機規范規定 Class 文件格式采用一種類似與 C 語言結構體的微結構體來存儲數據,這種偽結構體中只有兩種數據類型:無符號數和表。

  • 無符號數屬于基本的數據類型,以 u1、u2、u4、u8來分別代表 1 個字節、2 個字節、4 個字節和 8 個字節的無符號數,無符號數可以用來描述數字、索引引用、數量值或者按照 UTF-8 編碼結構構成的字符串值。

  • 是由多個無符號數或者其他表作為數據項構成的復合數據類型,所有表都習慣性地以「_info」結尾。表用于描述有層次關系的復合結構的數據,整個 Class 文件就是一張表,它由下表中所示的數據項構成。

類型名稱數量
u4magic1
u2minor_version1
u2major_version1
u2constant_pool_count1
cp_infoconstant_poolconstant_pool_count-1
u2access_flags1
u2this_class1
u2super_class1
u2interfaces_count1
u2interfacesinterfaces_count
u2fields_count1
field_infofieldsfields_count
u2methods_count1
method_infomethodsmethods_count
u2attributes_count1
attribute_infoattributesattributes_count

Class 文件中存儲的字節嚴格按照上表中的順序緊湊的排列在一起。哪個字節代表什么含義,長度是多少,先后順序如何都是被嚴格限制的,不允許有任何改變。

2.1 魔數與 Class 文件版本

每個 Class 文件的頭 4 個字節稱為魔數(Magic Number),它的唯一作用是確定這個文件是否為一個能被虛擬機接收的 Calss 文件。之所以使用魔數而不是文件后綴名來進行識別主要是基于安全性的考慮,因為文件后綴名是可以隨意更改的。Class 文件的魔數值為「0xCAFEBABE」。

緊接著魔數的 4 個字節存儲的是 Class 文件的版本號:第 5 和第 6 兩個字節是次版本號(Minor Version),第 7 和第 8 個字節是主版本號(Major Version)。高版本的 JDK 能夠向下兼容低版本的 Class 文件,虛擬機會拒絕執行超過其版本號的 Class 文件。

2.2 常量池

主版本號之后是常量池入口,常量池可以理解為 Class 文件之中的資源倉庫,它是 Class 文件結構中與其他項目關聯最多的數據類型,也是占用 Class 文件空間最大的數據項目之一,同是它還是 Class 文件中第一個出現的表類型數據項目。

因為常量池中常量的數量是不固定的,所以在常量池入口需要放置一個 u2 類型的數據來表示常量池的容量「constant_pool_count」,和計算機科學中計數的方法不一樣,這個容量是從 1 開始而不是從 0 開始計數。之所以將第 0 項常量空出來是為了滿足后面某些指向常量池的索引值的數據在特定情況下需要表達「不引用任何一個常量池項目」的含義,這種情況可以把索引值置為 0 來表示。

Class 文件結構中只有常量池的容量計數是從 1 開始的,其它集合類型,包括接口索引集合、字段表集合、方法表集合等容量計數都是從 0 開始。

常量池中主要存放兩大類常量:字面量符號引用。

  • 字面量比較接近 Java 語言層面的常量概念,如字符串、聲明為 final 的常量值等。

  • 符號引用屬于編譯原理方面的概念,包括了以下三類常量:

    • 類和接口的全限定名
    • 字段的名稱和描述符
    • 方法的名稱和描述符

2.3 訪問標志

緊接著常量池之后的兩個字節代表訪問標志(access_flag),這個標志用于識別一些類或者接口層次的訪問信息,包括這個 Class 是類還是接口;是否定義為 public 類型;是否定義為 abstract 類型;如果是類的話,是否被申明為 final 等。具體的標志位以及標志的含義見下表:

標志名稱標志值含義
ACC_PUBLIC0x0001是否為 public 類型
ACC_FINAL0x0010是否被聲明為 final,只有類可設置
ACC_SUPER0x0020是否允許使用 invokespecial 字節碼指令的新語意,invokespecial 指令的語意在 JKD 1.0.2 中發生過改變,微聊區別這條指令使用哪種語意,JDK 1.0.2 編譯出來的類的這個標志都必須為真
ACC_INTERFACE0x0200標識這是一個接口
ACC_ABSTRACT0x0400是否為 abstract 類型,對于接口或者抽象類來說,此標志值為真,其它類值為假
ACC_SYNTHETIC0x1000標識這個類并非由用戶代碼產生
ACC_ANNOTATION0x2000標識這是一個注解
ACC_ENUM0x4000標識這是一個枚舉

access_flags 中一共有 16 個標志位可以使用,當前只定義了其中的 8 個,沒有使用到的標志位要求一律為 0。

2.4 類索引、父類索引與接口索引集合

類索引(this_class)和父類索引(super_class)都是一個 u2 類型的數據,而接口索引集合(interfaces)是一組 u2 類型的數據集合,Class 文件中由這三項數據來確定這個類的繼承關系。

  • 類索引用于確定這個類的全限定名
  • 父類索引用于確定這個類的父類的全限定名
  • 接口索引集合用于描述這個類實現了哪些接口

2.5 字段表集合

字段表集合(field_info)用于描述接口或者類中聲明的變量。字段(field)包括類變量和實例變量,但不包括方法內部聲明的局部變量。下面我們看看字段表的結構:

類型名稱數量
u2access_flag1
u2name_index1
u2descriptor_index1
u2attributes_count1
attribute_infoattributesattributes_count

字段修飾符放在 access_flags 中,它與類中的 access_flag 非常相似,都是一個 u2 的數據類型。

標志名稱標志值含義
ACC_PUBLIC0x0001字段是否為 public
ACC_PRIVATE0x0002字段是否為 private
ACC_PROTECTED0x0004字段是否為 protected
ACC_STATIC0x0008字段是否為 static
ACC_FINAL0x0010字段是否為 final
ACC_VOLATILE0x0040字段是否為 volatile
ACC_TRANSIENT0x0080字段是否為 transient
ACC_SYNTHETIC0x1000字段是否由編譯器自動生成
ACC_ENUM0x4000字段是否為 enum

2.6 方法表集合

Class 文件中對方法的描述和對字段的描述是完全一致的,方法表中的結構和字段表的結構一樣。

因為 volatile 關鍵字和 transient 關鍵字不能修飾方法,所以方法表的訪問標志中沒有 ACC_VOLATILE 和 ACC_TRANSIENT。與之相對的,synchronizes、native、strictfp 和 abstract 關鍵字可以修飾方法,所以方法表的訪問標志中增加了 ACC_SYNCHRONIZED、ACC_NATIVE、ACC_STRICTFP 和 ACC_ABSTRACT 標志。

對于方法里的代碼,經過編譯器編譯成字節碼指令后,存放在方法屬性表中一個名為「Code」的屬性里面。

2.7 屬性表集合

在 Class 文件、字段表、方法表中都可以攜帶自己的屬性表(attribute_info)集合,用于描述某些場景專有的信息。

屬性表集合不像 Class 文件中的其它數據項要求這么嚴格,不強制要求各屬性表的順序,并且只要不與已有屬性名重復,任何人實現的編譯器都可以向屬性表中寫入自己定義的屬性信息,Java 虛擬機在運行時會略掉它不認識的屬性。

寫在最后

為了控制篇幅,這篇文章里丟棄了很多細節,比如常量池的項目類型、方法表、屬性表的具體內容等等。建議想要深入了解的同學可以自己動手將 Java 類編譯成二進制字節碼文件,根據文章里介紹的類文件結構逐個字符去對照和實驗,有助于加深理解。

關于「類文件結構」我們就介紹到這里,下一篇我們來聊聊「虛擬機的類加載機制」。

參考資料:

  • 《深入理解 Java 虛擬機:JVM 高級特性與最佳實踐(第 2 版)》

如果你喜歡我的文章,就關注下我的公眾號 BaronTalk 、 知乎專欄 或者在 GitHub 上添個 Star 吧!

  • 微信公眾號:BaronTalk
  • 知乎專欄:zhuanlan.zhihu.com/baron
  • GitHub:github.com/BaronZ88

轉載于:https://juejin.im/post/5d062bba6fb9a07ee742ddcc

總結

以上是生活随笔為你收集整理的深入理解Java虚拟机(类文件结构)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。