一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

一文讀懂Java的Class文件結(jié)構(gòu)

 山峰云繞 2023-08-21 發(fā)布于貴州


https://www.toutiao.com/article/7243069613830193668/?log_from=c3ea2be8487f1_1692607127801

1、前端編譯器和后端編譯器

Java源代碼的編譯結(jié)果是字節(jié)碼,那么肯定需要有一種編譯器能夠?qū)ava源碼編譯為字節(jié)碼,承擔這個重要責任的就是配置在path環(huán)境變量中的javac編譯器。javac是一種能夠?qū)ava源碼編譯為字節(jié)碼的前端編譯器。HotSpot并沒有強制要求前端編譯器只能使用javac來編譯字節(jié)碼,其實只要編譯結(jié)果符合JVM規(guī)范都可以被JVM所識別即可。在Java的前端編譯器領(lǐng)域,除了javac之外,還有一種被大家經(jīng)常用到的前端編譯器,那就是內(nèi)置在Eclipse中的ECJ(EclipseCompiler for Java)編譯器。和Javac的全量式編譯不同,ECJ是一種增量式編譯器。 在Eclipse中,當開發(fā)人員編寫完代碼后,使用“Ctr1+S”快捷鍵時,ECJ編譯器所采取的編譯方案是把未編譯部分的源碼逐行進行編譯,而非每次都全量編譯。因此EC3的編譯效率會比javac更加迅速和高效,當然編譯質(zhì)量和javac相比大致還是一樣的。ECJ不僅是Eclipse的默認內(nèi)置前端編譯器,在Tomcat中同樣也是使用ECJ編譯器來編譯jsp文件。由于ECJ編譯器是采用GPLv2的開源協(xié)議進行源代碼公開,所以,大家可以登錄ecliple官網(wǎng)下載ECJ編譯器的源碼進行二次開發(fā)。默認情況下,IntelliJ IDEA使用 javac編譯器。(還可以自己設(shè)置為AspectJ編譯器ajc)。前端編譯器并不會直接涉及編譯優(yōu)化等方面的技術(shù),而是將這些具體優(yōu)化細節(jié)移交給HotSpot的JIT編譯器負責。

2、學字節(jié)碼意義

通過字節(jié)碼分析,可以看到操作碼代碼執(zhí)行的細節(jié),對疑難問題排查定位更高效。

3、字節(jié)碼文件

3.1、字節(jié)碼文件

源代碼經(jīng)過編譯器編譯之后便會生成一個字節(jié)碼文件,字節(jié)碼是一種二進制的類文件,它的內(nèi)容是JVM的指令,而不像C、C++經(jīng)由編譯器直接生成機器碼。I

3.2、字節(jié)碼指令

Java虛擬機的指令由一個字節(jié)長度的、代表著某種特定操作含義的操作碼(opcode)以及跟隨其后的零至多個代表此操作所需參數(shù)的操作數(shù)( operand)所構(gòu)成。為了簡化,虛擬機中許多指令并不包含操作數(shù),只有一個操作碼。

3.3、查看二進制字節(jié)碼

3.3.1、使用Notepad++安裝HEX-Editor插件后打開

3.3.2、使用Binary Viewer軟件打開

4、Class文件結(jié)構(gòu)

4.1、字節(jié)碼文件結(jié)構(gòu)

官方文檔:
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html

4.2、 class文件格式

Class 的結(jié)構(gòu)不像XNL等描述語言,由于它沒有任何分隔符號。所以在其中的數(shù)據(jù)項,無論是字節(jié)順序還是數(shù)量,都是被嚴格限定的,哪個字節(jié)代表什么含義,長度是多少,先后順序如何,都不允許改變。Class 文件格式采用一種類似于C語言結(jié)構(gòu)體的方式進行數(shù)據(jù)存儲,這種結(jié)構(gòu)中只有兩種數(shù)據(jù)類型:無符號數(shù)和表。

  • 無符號數(shù)屬于基本的數(shù)據(jù)類型,以u1、u2、u4、u8 來分別代表1個字節(jié)、2個字節(jié)、4個字節(jié)和8個字節(jié)的無符號數(shù),無符號數(shù)可以用來描述數(shù)字、索引引用、數(shù)量值或者按照 UTF-8編碼構(gòu)成字符串值。
  • 表是由多個無符號數(shù)或者其他表作為數(shù)據(jù)項構(gòu)成的復合數(shù)據(jù)類型,所有表都習慣性地以“_info”結(jié)尾。表用于描述有層次關(guān)系的復合結(jié)構(gòu)的數(shù)據(jù),整個 Class 文件本質(zhì)上就是一張表。由于表沒有固定長度,所以通常會在其前面加上個數(shù)說明

5、魔數(shù)

  • 每個 Class 文件開頭的 4 個字節(jié)的無符號整數(shù)稱為魔數(shù)(Magic Number)
  • 它的唯一作用是確定這個文件是否為一個能被虛擬機接受的有效合法的 Class 文件。即:魔數(shù)是 Class 文件的標識符。
  • 魔數(shù)值固定為 0xCAFEBABE。不會改變。
  • 如果一個 Class 文件不以 0xCAFEBABE 開頭,虛擬機在進行文件校驗的時候就會直接拋出以下錯誤:
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 1885430635 in class file StringTest
  • 使用魔數(shù)而不是擴展名來進行識別主要是基于安全方面的考慮,因為文件擴展名可以隨意地改動。

6、文件版本號

緊接著魔數(shù)的 4 個字節(jié)存儲的是 Class 文件的版本號。同樣也是 4 個字節(jié)。第 5 個和第 6 個字節(jié)所代表的含義就是編譯的副版本號 minor_version,而第 7 個和第 8 個字節(jié)就是編譯的主版本號 major_version。它們共同構(gòu)成了 class 文件的格式版本號。譬如某個 Class 文件的主版本號為 M,副版本號為 m,那么這個 Class 文件的格式版本號就確定為 M.m。版本號和 Java 編譯器的對應關(guān)系如下表:

Java 的版本號是從 45 開始的,JDK1.1 之后的每個 JDK 大版本發(fā)布主版本號向上加 1。不同版本的 Java 編譯器編譯的 Class 文件對應的版本是不一樣的。目前,高版本的 Java 虛擬機可以執(zhí)行由低版本編譯器生成的 Class 文件,但是低版本的 Java 虛擬機不能執(zhí)行由高版本編譯器生成的 Class 文件。否則 JVM 會拋出
java.lang.UnsupportedClassVersionError 異常。(向下兼容)在實際應用中,由于開發(fā)環(huán)境和生產(chǎn)環(huán)境的不同,可能會導致該問題的發(fā)生。因此,需要我們在開發(fā)時,特別注意開發(fā)編譯的 JDK 版本和生產(chǎn)環(huán)境中的 JDK 版本是否一致。

7、常量池集合

常量池是 Class 文件中內(nèi)容最為豐富的區(qū)域之一。常量池對于 Class 文件中的字段和方法解析也有著至關(guān)重要的作用。隨著 Java 虛擬機的不斷發(fā)展,常量池的內(nèi)容也日漸豐富。可以說,常量池是整個 Class 文件的基石。在版本號之后,緊跟著的是常量池的數(shù)量,以及若干個常量池表項。常量池中常量的數(shù)量是不固定的,所以在常量池的入口需要放置一項 u2 類型的無符號數(shù),代表常量池容量計數(shù)值(constant_pool_count)。與 Java 中語言習慣不一樣的是,這個容量計數(shù)是從 1 而不是 0 開始的。因為它把第 0 項常量空出來了。這是為了滿足后面某些指向常量池的索引值的數(shù)據(jù)在特定情況下需要表達“不引用任何一個常量池項目”的含義,這種情況可用索引值 0 來表示。

Class 文件使用了一個前置的容量計數(shù)器(constant_pool_count)加若干個連續(xù)的數(shù)據(jù)項(constant_pool)的形式來描述常量池內(nèi)容。我們把這一系列連續(xù)常量池數(shù)據(jù)稱為常量池集合。常量池表項中,用于存放編譯時期生成的各種字面量和符號引用,這部分內(nèi)容將在類加載后進入方法區(qū)的運行時常量池中存放

7.1、常量池計數(shù)器

由于常量池的數(shù)量不固定,時長時短,所以需要放置兩個字節(jié)來表示常量池容量計數(shù)值。常量池容量計數(shù)值(u2 類型):從 1 開始,表示常量池中有多少項常量。即 constant_pool_count=1 表示常量池中有 0 個常量項。

7.2、常量池表

constant_pool 是一種表結(jié)構(gòu),以 1 ~ constant_pool_count - 1 為索引。表明了后面有多少個常量項。常量池主要存放兩大類常量:字面量(Literal)和符號引用(Symbolic References)。它包含了 class 文件結(jié)構(gòu)及其子結(jié)構(gòu)中引用的所有字符串常量、類或接口名、字段名和其他常量。常量池中的每一項都具備相同的特征。第 1 個字節(jié)作為類型標記,用于確定該項的格式,這個字節(jié)稱為 tag byte(標記字節(jié)、標簽字節(jié))。

字面量和符號引用 常量池主要存放兩大類常量:字面量(Literal)和符號引用(Symbolic References)。如下表:

常量

具體的常量

字面量

文本字符串,例:String str = "sun";

聲明為 final 的常量值,例:final int num=1;

符號引用

類和接口的全限定名

字段的名稱和描述符

方法的名稱和描述符

全限定名


com/sun/jvm02/ClassResolve這個就是類的全限定名,僅僅是把包名的“.“替換成”/”,為了使連續(xù)的多個全限定名之間不產(chǎn)生混淆,在使用時最后一般會加入一個“;”表示全限定名結(jié)束。

簡單名稱

簡單名稱是指沒有類型和參數(shù)修飾的方法或者字段名稱。

描述符

描述符的作用是用來描述字段的數(shù)據(jù)類型、方法的參數(shù)列表(包括數(shù)量、類型以及順序)和返回值。根據(jù)描述符規(guī)則,基本數(shù)據(jù)類型(byte、char、double、float、int、long、short、boolean)以及代表無返回值的 void 類型都用一個大寫字符來表示,而對象類型則用字符 L 加對象的全限定名來表示,詳見下表:

標志符

含義

B

基本數(shù)據(jù)類型 byte

C

基本數(shù)據(jù)類型 char

D

基本數(shù)據(jù)類型 double

F

基本數(shù)據(jù)類型 float

I

基本數(shù)據(jù)類型 int

J

基本數(shù)據(jù)類型 long

S

基本數(shù)據(jù)類型 short

Z

基本數(shù)據(jù)類型 boolean

V

代表 void 類型

L

對象類型,比如:Ljava/lang/Object;

[

數(shù)組類型,代表一維數(shù)組。比如:`double[] is [D

用描述符來描述方法時,按照先參數(shù)列表,后返回值的順序描述,參數(shù)列表按照參數(shù)的嚴格順序放在一組小括號“()”之內(nèi)。如方法 java.lang.String tostring()的描述符為()Ljava/lang/String; ,方法 int abc(int[]x, int y)的描述符為([II)I。

虛擬機在加載 Class 文件時才會進行動態(tài)鏈接,也就是說,Class 文件中不會保存各個方法和字段的最終內(nèi)存布局信息。因此,這些字段和方法的符號引用不經(jīng)過轉(zhuǎn)換是無法直接被虛擬機使用的。當虛擬機運行時,需要從常量池中獲得對應的符號引用,再在類加載過程中的解析階段將其替換為直接引用,并翻譯到具體的內(nèi)存地址中。

  • 符號引用:符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可。符號引用與虛擬機實現(xiàn)的內(nèi)存布局無關(guān),引用的目標并不一定已經(jīng)加載到了內(nèi)存中。
  • 直接引用:直接引用可以是直接指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。直接引用是與虛擬機實現(xiàn)的內(nèi)存布局相關(guān)的,同一個符號引用在不同虛擬機實例上翻譯出來的直接引用一般不會相同。如果有了直接引用,那說明引用的目標必定已經(jīng)存在于內(nèi)存之中了。

常量池中每一項常量都是一個表,J0K1.7 之后共有 14 種不同的表結(jié)構(gòu)數(shù)據(jù)。根據(jù)上圖每個類型的描述我們也可以知道每個類型是用來描述常量池中哪些內(nèi)容(主要是字面量、符號引用)的。比如: CONSTANT_Integer_info 是用來描述常量池中字面量信息的,而且只是整型字面量信息。標志為 15、16、18 的常量項類型是用來支持動態(tài)語言調(diào)用的(jdk1.7 時才加入的)。

常量池總結(jié) :

這 14 種表(或者常量項結(jié)構(gòu))的共同點是:表開始的第一位是一個 u1 類型的標志位(tag),代表當前這個常量項使用的是哪種表結(jié)構(gòu),即哪種常量類型。 在常量池列表中,CONSTANT_Utf8_info 常量項是一種使用改進過的 UTF-8 編碼格式來存儲諸如文字字符串、類或者接口的全限定名、字段或者方法的簡單名稱以及描述符等常量字符串信息。 這 14 種常量項結(jié)構(gòu)還有一個特點是,其中 13 個常量項占用的字節(jié)固定,只有 CONSTANT_Utf8_info 占用字節(jié)不固定,其大小由 length 決定。為什么呢?因為從常量池存放的內(nèi)容可知,其存放的是字面量和符號引用,最終這些內(nèi)容都會是一個字符串,這些字符串的大小是在編寫程序時才確定,比如你定義一個類,類名可以取長取短,所以在沒編譯前,大小不固定,編譯后,通過 utf-8 編碼,就可以知道其長度。

常量池:可以理解為 Class 文件之中的資源倉庫,它是 Class 文件結(jié)構(gòu)中與其他項目關(guān)聯(lián)最多的數(shù)據(jù)類型(后面的很多數(shù)據(jù)類型都會指向此處),也是占用 Class 文件空間最大的數(shù)據(jù)項目之一。 Java 代碼在進行 Javac 編譯的時候,并不像 C 和 C++那樣有“連接”這一步驟,而是在虛擬機加載 C1ass 文件的時候進行動態(tài)鏈接。也就是說,在 Class 文件中不會保存各個方法、字段的最終內(nèi)存布局信息,因此這些字段、方法的符號引用不經(jīng)過運行期轉(zhuǎn)換的話無法得到真正的內(nèi)存入口地址,也就無法直接被虛擬機使用。當虛擬機運行時,需要從常量池獲得對應的符號引用,再在類創(chuàng)建時或運行時解析、翻譯到具體的內(nèi)存地址之中。

8、訪問標志

在常量池后,緊跟著訪問標記。該標記使用兩個字節(jié)表示,用于識別一些類或者接口層次的訪問信息,包括:這個 Class 是類還是接口;是否定義為 public 類型;是否定義為 abstract 類型;如果是類的話,是否被聲明為 final 等。各種訪問標記如下所示:

標志名稱

標志值

含義

ACC_PUBLIC

0x0001

標志為 public 類型

ACC_FINAL

0x0010

標志被聲明為 final,只有類可以設(shè)置

ACC_SUPER

0x0020

標志允許使用 invokespecial 字節(jié)碼指令的新語義,JDK1.0.2 之后編譯出來的類的這個標志默認為真。(使用增強的方法調(diào)用父類方法)

ACC_INTERFACE

0x0200

標志這是一個接口

ACC_ABSTRACT

0x0400

是否為 abstract 類型,對于接口或者抽象類來說,次標志值為真,其他類型為假

ACC_SYNTHETIC

0x1000

標志此類并非由用戶代碼產(chǎn)生(即:由編譯器產(chǎn)生的類,沒有源碼對應)

ACC_ANNOTATION

0x2000

標志這是一個注解

ACC_ENUM

0x4000

標志這是一個枚舉

類的訪問權(quán)限通常為 ACC_開頭的常量。每一種類型的表示都是通過設(shè)置訪問標記的 32 位中的特定位來實現(xiàn)的。比如,若是 public final 的類,則該標記為 ACC_PUBLIC | ACC_FINAL。使用 ACC_SUPER 可以讓類更準確地定位到父類的方法 super.method(),現(xiàn)代編譯器都會設(shè)置并且使用這個標記。

  • 帶有 ACC_INTERFACE 標志的 class 文件表示的是接口而不是類,反之則表示的是類而不是接口。
  • 如果一個 class 文件被設(shè)置了 ACC_INTERFACE 標志,那么同時也得設(shè)置 ACC_ABSTRACT 標志。同時它不能再設(shè)置 ACC_FINAL、ACC_SUPER 或 ACC_ENUM 標志。
  • 如果沒有設(shè)置 ACC_INTERFACE 標志,那么這個 class 文件可以具有上表中除 ACC_ANNOTATION 外的其他所有標志。當然,ACC_FINAL 和 ACC_ABSTRACT 這類互斥的標志除外。這兩個標志不得同時設(shè)置。
  • ACC_SUPER 標志用于確定類或接口里面的 invokespecial 指令使用的是哪一種執(zhí)行語義。針對 Java 虛擬機指令集的編譯器都應當設(shè)置這個標志。對于 Java SE 8 及后續(xù)版本來說,無論 class 文件中這個標志的實際值是什么,也不管 class 文件的版本號是多少,Java 虛擬機都認為每個 class 文件均設(shè)置了 ACC_SUPER 標志。
  • ACC_SUPER 標志是為了向后兼容由舊 Java 編譯器所編譯的代碼而設(shè)計的。目前的 ACC_SUPER 標志在由 JDK1.0.2 之前的編譯器所生成的 access_flags 中是沒有確定含義的,如果設(shè)置了該標志,那么 0racle 的 Java 虛擬機實現(xiàn)會將其忽略。
  • ACC_SYNTHETIC 標志意味著該類或接口是由編譯器生成的,而不是由源代碼生成的。
  • 注解類型必須設(shè)置 ACC_ANNOTATION 標志。如果設(shè)置了 ACC_ANNOTATION 標志,那么也必須設(shè)置 ACC_INTERFACE 標志。
  • ACC_ENUM 標志表明該類或其父類為枚舉類型。

9、類索引、父類索引、接口索引

在訪問標記后,會指定該類的類別、父類類別以及實現(xiàn)的接口,格式如下:

長度

含義

u2

this_class

u2

super_class

u2

interfaces_count

u2

interfaces[interfaces_count]

類索引、父類索引、接口索引這三項數(shù)據(jù)來確定這個類的繼承關(guān)系:

  • 類索引用于確定這個類的全限定名
  • 父類索引用于確定這個類的父類的全限定名。由于 Java 語言不允許多重繼承,所以父類索引只有一個,除了 java.lang.Object 之外,所有的 Java 類都有父類。
  • 接口索引集合就用來描述這個類實現(xiàn)了哪些接口,這些被實現(xiàn)的接口將按 implements 語句(如果這個類本身是一個接口,則應當是 extends 語句)后的接口順序從左到右排列在接口索引集合中。

9.1、this_class(類索引)

2 字節(jié)無符號整數(shù),指向常量池的索引。它提供了類的全限定名,如
com/sun/jvm02/ClassResolve。this_class 的值必須是對常量池表中某項的一個有效索引值。常量池在這個索引處的成員必須為 CONSTANT_Class_info 類型結(jié)構(gòu)體,該結(jié)構(gòu)體表示這個 class 文件所定義的類或接口。

9.2、super_class(父類索引)

2 字節(jié)無符號整數(shù),指向常量池的索引。它提供了當前類的父類的全限定名。如果我們沒有繼承任何類,其默認繼承的是 java/lang/object 類。同時,由于 Java 不支持多繼承,所以其父類只有一個。super_class 指向的父類不能是 final。

9.3、interfaces

指向常量池索引集合,它提供了一個符號引用到所有已實現(xiàn)的接口

由于一個類可以實現(xiàn)多個接口,因此需要以數(shù)組形式保存多個接口的索引,表示接口的每個索引也是一個指向常量池的 CONSTANT_Class(當然這里就必須是接口,而不是類)。

  • interfaces_count(接口計數(shù)器):表示當前類或接口的直接超接口數(shù)量。
  • interfaces[](接口索引集合):每個成員的值必須是對常量池表中某項的有效索引值,它的長度為 interfaces_count。每個成員 interfaces[i]必須為 CONSTANT_Class_info 結(jié)構(gòu),其中 0 <= i < interfaces_count。在 interfaces[]中,各成員所表示的接口順序和對應的源代碼中給定的接口順序(從左至右)一樣,即 interfaces[0]對應的是源代碼中最左邊的接口。

后續(xù)介紹字段表集合、方法表集合、屬性表集合。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    出差被公高潮久久中文字幕| 亚洲精品有码中文字幕在线观看| 国产成人精品在线一区二区三区| 欧美成人精品国产成人综合| 婷婷色网视频在线播放| 日本二区三区在线播放| 两性色午夜天堂免费视频| 伊人久久青草地综合婷婷| 69精品一区二区蜜桃视频| 香蕉尹人视频在线精品| 日韩欧美91在线视频| 国产欧美日产久久婷婷| 欧美黑人巨大一区二区三区| 青青操精品视频在线观看| 欧美日韩综合在线第一页| 中文字幕欧美视频二区| 日本办公室三级在线观看| 国产亚洲欧美日韩国亚语| 欧美乱妇日本乱码特黄大片 | 天海翼高清二区三区在线| 熟女体下毛荫荫黑森林自拍| 国产精品欧美激情在线播放| 国产又黄又爽又粗视频在线| 国产乱人伦精品一区二区三区四区| 日韩日韩日韩日韩在线| 国产免费黄片一区二区| 四季精品人妻av一区二区三区 | 五月天六月激情联盟网| 亚洲精品成人福利在线| 精品高清美女精品国产区| 久久91精品国产亚洲| 中国少妇精品偷拍视频 | 91欧美亚洲精品在线观看| 又黄又色又爽又免费的视频| 国产成人免费激情视频| 偷拍美女洗澡免费视频| 好吊色欧美一区二区三区顽频| 国产欧美一区二区三区精品视 | av国产熟妇露脸在线观看| 日本精品视频一二三区| 不卡一区二区高清视频|