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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

读《深入jvm原理》之class文件

發(fā)布時(shí)間:2023/12/19 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 读《深入jvm原理》之class文件 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
class文件是什么?? 是二進(jìn)制文件,是被jvm識(shí)別的二進(jìn)制文件。這是Java初學(xué)者的回答。

較為深入的學(xué)習(xí)者,可能會(huì)給出跨語(yǔ)言編程的概念和實(shí)現(xiàn),class文件就是一個(gè)跨語(yǔ)言的實(shí)現(xiàn)的第一步,也是動(dòng)態(tài)修改整理已完成編碼的代碼,去生成新代碼的指令的劃時(shí)代編程的第一步(動(dòng)態(tài)類(lèi)生成技術(shù))。


動(dòng)態(tài)生成類(lèi)技術(shù):



class文件是一種8位字節(jié)的二進(jìn)制流文件, 各個(gè)數(shù)據(jù)項(xiàng)按順序緊密的從前向后排列, 相鄰的項(xiàng)之間沒(méi)有間隙, 這樣可以使得class文件非常緊湊, 體積輕巧, 可以被JVM快速的加載至內(nèi)存, 并且占據(jù)較少的內(nèi)存空間。 我們的Java源文件, 在被編譯之后, 每個(gè)類(lèi)(或者接口)都單獨(dú)占據(jù)一個(gè)class文件, 并且類(lèi)中的所有信息都會(huì)在class文件中有相應(yīng)的描述, 由于class文件很靈活, 它甚至比Java源文件有著更強(qiáng)的描述能力。
class文件中的信息是一項(xiàng)一項(xiàng)排列的, 每項(xiàng)數(shù)據(jù)都有它的固定長(zhǎng)度, 有的占一個(gè)字節(jié), 有的占兩個(gè)字節(jié), 還有的占四個(gè)字節(jié)或8個(gè)字節(jié), 數(shù)據(jù)項(xiàng)的不同長(zhǎng)度分別用u1, u2, u4, u8表示, 分別表示一種數(shù)據(jù)項(xiàng)在class文件中占據(jù)一個(gè)字節(jié), 兩個(gè)字節(jié), 4個(gè)字節(jié)和8個(gè)字節(jié)。 可以把u1, u2, u3, u4看做class文件數(shù)據(jù)項(xiàng)的“類(lèi)型” 。
class文件中存在以下數(shù)據(jù)項(xiàng)(該圖表參考自《深入Java虛擬機(jī)》):
類(lèi)型名稱(chēng)數(shù)量
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
u2attribute_count1
attribute_infoattributesattributes_count


下面對(duì)class文件中的每一項(xiàng)進(jìn)行詳細(xì)的解釋。fields(域,字段或者信息組,包含方法和屬性)


class文件中的魔數(shù)和版本號(hào)


(1) magic

在class文件開(kāi)頭的四個(gè)字節(jié), 存放著class文件的魔數(shù), 這個(gè)魔數(shù)是class文件的標(biāo)志,他是一個(gè)固定的值: 0XCAFEBABE 。 也就是說(shuō)他是判斷一個(gè)文件是不是class格式的文件的標(biāo)準(zhǔn), 如果開(kāi)頭四個(gè)字節(jié)不是0XCAFEBABE, 那么就說(shuō)明它不是class文件, 不能被JVM識(shí)別。


(2)minor_version 和 major_version

緊接著魔數(shù)的四個(gè)字節(jié)是class文件的此版本號(hào)和主版本號(hào)。 隨著Java的發(fā)展, class文件的格式也會(huì)做相應(yīng)的變動(dòng)。 版本號(hào)標(biāo)志著class文件在什么時(shí)候, 加入或改變了哪些特性。 舉例來(lái)說(shuō), 不同版本的javac編譯器編譯的class文件, 版本號(hào)可能不同, 而不同版本的JVM能識(shí)別的class文件的版本號(hào)也可能不同, 一般情況下, 高版本的JVM能識(shí)別低版本的javac編譯器編譯的class文件, 而低版本的JVM不能識(shí)別高版本的javac編譯器編譯的class文件。 如果使用低版本的JVM執(zhí)行高版本的class文件,?JVM會(huì)拋出java.lang.UnsupportedClassVersionError 。具體的版本號(hào)變遷這里不再討論, 需要的讀者自行查閱資料。?


class文件中的常量池概述


在class文件中, 位于版本號(hào)后面的就是常量池相關(guān)的數(shù)據(jù)項(xiàng)。 常量池是class文件中的一項(xiàng)非常重要的數(shù)據(jù)。 常量池中存放了文字字符串, 常量值, 當(dāng)前類(lèi)的類(lèi)名, 字段名, 方法名, 各個(gè)字段和方法的描述符, 對(duì)當(dāng)前類(lèi)的字段和方法的引用信息, 當(dāng)前類(lèi)中對(duì)其他類(lèi)的引用信息等等。?常量池中幾乎包含類(lèi)中的所有信息的描述, class文件中的很多其他部分都是對(duì)常量池中的數(shù)據(jù)項(xiàng)的引用,比如后面要講到的this_class, super_class, field_info, attribute_info等, 另外字節(jié)碼指令中也存在對(duì)常量池的引用, 這個(gè)對(duì)常量池的引用當(dāng)做字節(jié)碼指令的一個(gè)操作數(shù)。 ?此外, 常量池中各個(gè)項(xiàng)也會(huì)相互引用。


class文件中的項(xiàng)constant_pool_count的值為1, 說(shuō)明每個(gè)類(lèi)都只有一個(gè)常量池。 常量池中的數(shù)據(jù)也是一項(xiàng)一項(xiàng)的, 沒(méi)有間隙的依次排放。常量池中各個(gè)數(shù)據(jù)項(xiàng)通過(guò)索引來(lái)訪問(wèn), 有點(diǎn)類(lèi)似與數(shù)組, 只不過(guò)常量池中的第一項(xiàng)的索引為1, 而不為0, 如果class文件中的其他地方引用了索引為0的常量池項(xiàng), 就說(shuō)明它不引用任何常量池項(xiàng)。class文件中的每一種數(shù)據(jù)項(xiàng)都有自己的類(lèi)型, 相同的道理,常量池中的每一種數(shù)據(jù)項(xiàng)也有自己的類(lèi)型。 常量池中的數(shù)據(jù)項(xiàng)的類(lèi)型如下表:

常量池中數(shù)據(jù)項(xiàng)類(lèi)型類(lèi)型標(biāo)志類(lèi)型描述
CONSTANT_Utf81UTF-8編碼的Unicode字符串
CONSTANT_Integer3int類(lèi)型字面值
CONSTANT_Float4float類(lèi)型字面值
CONSTANT_Long5long類(lèi)型字面值
CONSTANT_Double6double類(lèi)型字面值
CONSTANT_Class7對(duì)一個(gè)類(lèi)或接口的符號(hào)引用
CONSTANT_String8String類(lèi)型字面值
CONSTANT_Fieldref9對(duì)一個(gè)字段的符號(hào)引用
CONSTANT_Methodref10對(duì)一個(gè)類(lèi)中聲明的方法的符號(hào)引用
CONSTANT_InterfaceMethodref11對(duì)一個(gè)接口中聲明的方法的符號(hào)引用
CONSTANT_NameAndType12對(duì)一個(gè)字段或方法的部分符號(hào)引用

每個(gè)數(shù)據(jù)項(xiàng)叫做一個(gè)XXX_info項(xiàng), 比如, 一個(gè)常量池中一個(gè)CONSTANT_Utf8類(lèi)型的項(xiàng), 就是一個(gè)CONSTANT_Utf8_info 。除此之外, 每個(gè)info項(xiàng)中都有一個(gè)標(biāo)志值(tag), 這個(gè)標(biāo)志值表明了這個(gè)常量池中的info項(xiàng)的類(lèi)型是什么, 從上面的表格中可以看出, 一個(gè)CONSTANT_Utf8_info中的tag值為1, 而一個(gè)CONSTANT_Fieldref_info中的tag值為9 。

Java程序是動(dòng)態(tài)鏈接的, 在動(dòng)態(tài)鏈接的實(shí)現(xiàn)中, 常量池扮演者舉足輕重的角色。 除了存放一些字面量之外, 常量池中還存放著以下幾種符號(hào)引用:

(1) 類(lèi)和接口的全限定名

(2) 字段的名稱(chēng)和描述符

(3) 方法的名稱(chēng)和描述符

在詳細(xì)講解常量池中的各個(gè)數(shù)據(jù)項(xiàng)之前, 我們有必要先了解一下class文件中的特殊字符串, 因?yàn)樵诔A砍刂?#xff0c; 特殊字符串大量的出現(xiàn),這些特殊字符串就是上面說(shuō)的全限定名和描述符。 要理解常量池中的各個(gè)數(shù)據(jù)項(xiàng), 必須先了解這些特殊字符串。


class文件中的特殊字符串


首先說(shuō)明一下, 所謂的特殊字符串出現(xiàn)在class文件中的常量池中, 所以在上一篇博客中, 只是對(duì)常量池介紹了一個(gè)大概。 本著循序漸進(jìn)和減少跨度的原則, 首先把class文件中的特殊字符串做一個(gè)詳細(xì)的介紹, 然后再回過(guò)頭來(lái)繼續(xù)講解常量池。?
在上文中, 我們提到特殊字符串是常量池中符號(hào)引用的一部分, 至于符號(hào)引用的概念, 會(huì)在以后提到。 現(xiàn)在我們將重點(diǎn)放在特殊字符串上。 特殊字符串包括三種: 類(lèi)的全限定名, 字段和方法的描述符, 特殊方法的方法名。 下面我們就分別介紹這三種特殊字符串。

(1) 類(lèi)的全限定名


在常量池中, 一個(gè)類(lèi)型的名字并不是我們?cè)谠次募锌吹降哪菢?#xff0c; 也不是我們?cè)谠次募惺褂玫陌宇?lèi)名的形式。 源文件中的全限定名和class文件中的全限定名不是相同的概念。 源文件中的全新定名是包名加類(lèi)名, 包名的各個(gè)部分之間,包名和類(lèi)名之間, 使用點(diǎn)號(hào)分割。 如Object類(lèi), 在源文件中的全限定名是java.lang.Object 。 而class文件中的全限定名是將點(diǎn)號(hào)替換成“/” 。 例如, Object類(lèi)在class文件中的全限定名是 java/lang/Object 。 如果讀者之前沒(méi)有接觸過(guò)class文件格式, 是class文件格式的初學(xué)者, 在這里不必知道全限定名在class文件中是如何使用的, 只需要知道, 源文件中一個(gè)類(lèi)的名字, 在class文件中是用全限定名表述的。?

(2) 描述符


我們知道在一個(gè)類(lèi)中可以有若干字段和方法, 這些字段和方法在源文件中如何表述, 我們?cè)偈煜げ贿^(guò)了。 既然現(xiàn)在我們要學(xué)習(xí)class文件格式, 那么我們就要問(wèn), 一個(gè)字段或一個(gè)方法在class文件中是如何表述的? 在本文中, 我們會(huì)討論方法和字段在class文件中的描述。 方法和字段的描述符并不會(huì)把方法和字段的所有信息全都描述出來(lái), 畢竟描述符只是一個(gè)簡(jiǎn)單的字符串。?
在講解描述符之前, 要先說(shuō)明一個(gè)問(wèn)題, 那就是所有的類(lèi)型在描述符中都有對(duì)應(yīng)的字符或字符串來(lái)對(duì)應(yīng)。 比如, 每種基本數(shù)據(jù)類(lèi)型都有一個(gè)大寫(xiě)字母做對(duì)應(yīng), void也有一個(gè)大寫(xiě)字符做對(duì)應(yīng)。 下表是void和基本數(shù)據(jù)類(lèi)型在描述符中的對(duì)應(yīng)。
基本數(shù)據(jù)類(lèi)型和void類(lèi)型類(lèi)型的對(duì)應(yīng)字符
byteB
charC
doubleD
floatF
intI
longJ
shortS
booleanZ
voidV


基本上都是以類(lèi)型的首字符變成大寫(xiě)來(lái)對(duì)應(yīng)的, 其中l(wèi)ong和boolean是特例, long類(lèi)型在描述符中的對(duì)應(yīng)字符是J, boolean類(lèi)型在描述符中的對(duì)應(yīng)字符是Z 。?
基本類(lèi)型和void在描述符中都有一個(gè)大寫(xiě)字符和他們對(duì)應(yīng), 那么引用類(lèi)型(類(lèi)和接口,枚舉)在描述符中是如何對(duì)應(yīng)的呢? 引用類(lèi)型的對(duì)應(yīng)字符串(注意, 引用類(lèi)型在描述符中使用一個(gè)字符串做對(duì)應(yīng)) , 這個(gè)字符串的格式是: “L” + 類(lèi)型的全限定名 + “;”
注意,這三個(gè)部分之間沒(méi)有空格, 是緊密排列的。 如Object在描述符中的對(duì)應(yīng)字符串是: Ljava/lang/Object; ?; ArrayList在描述符中的對(duì)應(yīng)字符串是: Ljava/lang/ArrayList; ?; 自定義類(lèi)型com.example.Person在描述符中的對(duì)應(yīng)字符串是: Lcom/example/Person; 。
我們知道, 在Java語(yǔ)言中數(shù)組也是一種類(lèi)型, 一個(gè)數(shù)組的元素類(lèi)型和他的維度決定了他的類(lèi)型。 比如, 在 int[] a 聲明中, 變量a的類(lèi)型是int[] , 在 int[][] b 聲明中, 變量b的類(lèi)型是int[][] , 在 Object[] c 聲明中, 變量c的類(lèi)型是Object[] 。既然數(shù)組是類(lèi)型, 那么在描述符中, 也應(yīng)該有數(shù)組類(lèi)型的對(duì)應(yīng)字符串。 在class文件的描述符中, 數(shù)組的類(lèi)型中每個(gè)維度都用一個(gè) [ 代表, 數(shù)組類(lèi)型整個(gè)類(lèi)型的對(duì)應(yīng)字符串的格式如下: 若干個(gè)“[” + 數(shù)組中元素類(lèi)型的對(duì)應(yīng)字符串

下面舉例來(lái)說(shuō)名。?int[]類(lèi)型的對(duì)應(yīng)字符串是: [I ;int[][]類(lèi)型的對(duì)應(yīng)字符串是: [[I ;Object[]類(lèi)型的對(duì)應(yīng)字符串是: [Ljava/lang/Objec;Object[][][]類(lèi)型的對(duì)應(yīng)字符串是: [[[Ljava/lang/Object。
介紹完每種類(lèi)型在描述符中的對(duì)應(yīng)字符串, 下面就開(kāi)始講解字段和方法的描述符。?
字段的描述符就是字段的類(lèi)型所對(duì)應(yīng)的字符或字符串。 如: int i 中, 字段i的描述符就是 I ;Object o中, 字段o的描述符就是 Ljava/lang/Object;double[][] d中, 字段d的描述符就是 [[D 。?
方法的描述符比較復(fù)雜, 包括所有參數(shù)的類(lèi)型列表和方法返回值。 它的格式是這樣的: (參數(shù)1類(lèi)型 參數(shù)2類(lèi)型 參數(shù)3類(lèi)型 ...)返回值類(lèi)型 其中, 不管是參數(shù)的類(lèi)型還是返回值類(lèi)型, 都是使用對(duì)應(yīng)字符和對(duì)應(yīng)字符串來(lái)表示的, 并且參數(shù)列表使用小括號(hào)括起來(lái), 并且各個(gè)參數(shù)類(lèi)型之間沒(méi)有空格, 參數(shù)列表和返回值類(lèi)型之間也沒(méi)有空格。?
下面舉例說(shuō)明(此表格來(lái)源于《深入Java虛擬機(jī)》)。
方法描述符方法聲明
()Iint getSize()
()Ljava/lang/String;String toString()
([Ljava/lang/String;)Vvoid main(String[] args)
()Vvoid wait()
(JI)Vvoid wait(long timeout, int nanos)
(ZILjava/lang/String;II)Zboolean regionMatches(boolean ignoreCase, int toOffset, String other, int ooffset, int len)
([BII)Iint read(byte[] b, int off, int len )
()[[Ljava/lang/Object;Object[][] getObjectArray()

?

(3) 特殊方法的方法名


首先要明確一下, 這里的特殊方法是指的類(lèi)的構(gòu)造方法和類(lèi)型初始化方法。 構(gòu)造方法就不用多說(shuō)了, 至于類(lèi)型的初始化方法, 對(duì)應(yīng)到源碼中就是靜態(tài)初始化塊。 也就是說(shuō), 靜態(tài)初始化塊, 在class文件中是以一個(gè)方法表述的, 這個(gè)方法同樣有方法描述符和方法名。?
類(lèi)的構(gòu)造方法的方法名使用字符串 <init> 表示, 而靜態(tài)初始化方法的方法名使用字符串?<clinit> 表示。 除了這兩種特殊的方法外, 其他普通方法的方法名, 和源文件中的方法名相同。


總結(jié)

以上是生活随笔為你收集整理的读《深入jvm原理》之class文件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。