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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

class字节码文件中的常量池结构详解

發布時間:2024/9/30 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 class字节码文件中的常量池结构详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 前言
  • 方法區
  • 常量池基本結構
  • JVM 所定義的11種常量
  • 常量池元素的復合結構
  • 常量池的結束位置
  • 常量池元素總數量
  • 第一個常量池元素
  • 父類常量
  • 變量型常量池元素

自己的學習筆記,部分節選自《揭秘java虛擬機》


前言

對于一個class文件,內容有:

以u1、u2、u4、u8分別代表1個字節、2個字節、4個字節、8個字節的無符號數

這里主要說class文件中的常量池:
constant_pool_count
常量池計數器,constant_pool_count的值等于constant_pool表中的成員數加1。constant_pool 表的索引值只有在大于 0 且小于 constant_pool_count 時才會被認為是有效的 ,對于 long 和 double 類型有例外情況。

注意:雖然值為 0 的 constant_pool 索引是無效的,但其他用到常量池的數據結構可以使用索引 0 來表示“不引用任何一個常量池項”的意思。

constant_pool[ ]
常量池,constant_pool 是一種表結構,它包含 Class 文件結構及其子結構中引用的所有字符串常量、類或接口名、字段名和其它常量。常量池中的每一項都具備相同的格式特征——第一個字節作為類型標記用于識別該項是哪種類型的常量,稱為“tagbyte”。常量池的索引范圍是 1 至 constant_pool_count?1。

Jdk1.6及之前:有永久代, 常量池在方法區
Jdk1.7:有永久代,但已經逐步“去永久代”,常量池在堆
Jdk1.8及之后: 無永久代,常量池在元空間

常量池中主要存放兩大類常量:字面量(Literal)和符號引用(Symbolic References)。字面量比較接近于Java語言層面的常量概念,如文本字符串、被聲明為final的常量值等。而符號引用則屬于編譯原理方面的概念,主要包括下面幾類常量:

·被模塊導出或者開放的包(Package)

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

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

·方法的名稱和描述符

·方法句柄和方法類型(Method Handle、Method Type、Invoke Dynamic)

·動態調用點和動態常量(Dynamically-Computed Call Site、Dynamically-Computed Constant)

方法區

既然提到常量池就順便提一下方法區,方法區主要保存的信息是類的元數據。方法區與堆空間類似,它也是被JVM中所有的線程共享的區域。方法區中最為重要的是類的類型信息、常量池、域信息、方法信息。類型信息包括類的完整名稱、父類的完整名稱、類型修飾符(public/protected/private)和類型的直接接口類表。

常量池基本結構

Java類所對應的常量池主要由常量池數量和常量池數組兩部分組成(如下圖所示),常量池數量緊跟在次版本號的后面,占2字節。常量池數組則緊跟在常量池數量之后。 常量池數組,顧名思義,就是一個類似數組的結構。這個數組固化在字節碼文件中,由多個元素組成。與一般數組概念不同的是,常量池數組中不同的元素的類型、結構都是不同的,長度也是不同的,但是每一種元素的第一個數據都是一個u1類型,該字節是標志位,占1個字節(如圖4.4所示)。JVM解析常量池時,根據這個u1類型來獲取該元素的具體類型。常量池的結構組成如圖所示。

使用結構化的方式來描述常量池數組的編排,可以如下描述: tag1元素內容1 tag2元素內容2 tag3元素內容3 … tagn元素內容n 這里為了描述,在tag與元素內容之間添加了空格,但實際的class 二進制文件中,這些tag與元素內容之間絕沒有任何空格信息,也沒有任何其他的多余信息,tag后面緊跟著元素內容,第一個元素內容結束了,緊接著就是第二個元素的tag信息,由此可見,整個字節碼文件的格式設計都是非常緊湊的。

JVM 所定義的11種常量

常量池元素中的不同元素結構與類型都是不同的,正因如此,JVM只能定義有限的元素類型,并針對有限的類型進行專門解析。JVM一共定義了11種常量,
 JVM常量池元素一覽表  

常量池元素的復合結構

常量池數組中的每一種元素的內容都是復合數據結構的,下面分別給出JVM所定義的常量池中每一種元素的具體結構。 該表中tag值為1的常量池元素CONSTANT_Utf8_info,其組成結構為3部分,分別是:tag、length和bytes,其中tag和length的長度分別是u1、u2,即分別占1字節和2字節。而bytes則是字符串的具體內容,其長度是length字節。在字節碼文件中,該常量池元素最終所占的字節數是: 1 + 2 + length 其他類型的常量池元素的組成結構類似。
常量池元素結構:

可以看到,類的方法信息、接口和繼承信息、屬性信息都是定義在NamedAndType_Info中的。

常量池的結束位置

相信有不少讀者讀到這里,可能潛意識里會突然蹦出這么一個問題:整個字節碼文件由多個部分構成,常量池數組只是其中一塊,JVM在解析字節碼文件時,一定需要分別讀取各個部分的字節流,其中也包括常量池數組。但是常量池中有部分元素的值是bytes數組,其長度是隨機變化的,那么JVM在解析時,是如何知道整個常量池的信息解析到什么位置結束呢?其實經過分析不難發現,首先,class文件給出了常量池的總數;其次,凡是碰到有bytes數組的常量池元素,class文件在常量池的每一個元素之前都會專門劃分出2字節用于描述該常量池元素內容所占的字節長度,這樣一來,常量池中每一個元素的長度是確定的,而常量池的總數也是確定的,JVM據此便可以從class文件中準確地計算出常量池結構體的末端位置(起始位置不用計算,是定死的,從第9字節開始,前面8字節分別是魔數和版本號)。

常量池元素總數量

前面8字節用于描述魔數和版本號,從第9字節開始的一大段字節流都用于描述常量池數組信息。其中,第9和第10字節用于描述常量池元素的總數量。

第9和第10字節所保存的常量池數組大小是0x33,換算成十進制是51,說明該字節碼文件中一共包含51個常量池元素。JVM規定,不使用第0個元素,因此實際上一共有50個常量池元素(下文在解析源碼時,會看到源碼中的確是從第1個元素開始解析的,而不是從第0個)。

第一個常量池元素

常量池數量之后(即從第11字節開始),就是常量池數組。每一個常量池元素都以tag位標開始,tag位標都只占1字節長度。如圖所示。

第11字節對應的值是7,對照上文所給的常量池11種元素的復合結構可知,tag位標為7代表的是CONSTANT_Class_info,即類或接口的符號引用,這種類型的元素的結構組成如下:
◎ tag位標 占1字節
◎ index 占2字節
既然tag位標已經占據了字節碼文件的第11字節,則接下來的第12和13字節將合起來表示index。如上圖所示,這兩個字節對應的值為2。

從第11字節開始,到第13字節為止,一個常量池元素就被描述完成。緊接著開始描述第2個常量池元素

第一個常量池元素后面緊跟著的是第二個常量池元素。其第一字節是01,表示這是一個UTF8編碼的字符串,其結構是:
◎ tag位,占1字節
◎ length,占2字節
◎ bytes,占length字節
如圖所示,tag位后面的2字節的值是4,表示bytes占4字節,其值為0x54657374,其中每兩字節正好代表一個字符,對應的字符串是Test。

*

父類常量

剛才的第1與第2兩個常量用于描述Java類型信息,接下來的第3與第4兩個常量則用于描述父類信息。由于Test.Java類并沒有顯式繼承任何類,因此編譯后處理成默認繼承,即父類是Java.lang.Object。
父類常量的tag位也是07,其類名是java/lang/Object
下面分別給出第3和第4這兩個常量在文件中的內容。

圖4.9顯示,字符串的length值為0x10,即16,于是其后面的16字節都是bytes。由此可以進一步驗證,字符串常量的結構由tag、length和bytes組成,bytes的長度由length指定。

變量型常量池元素

常量池中前面4個元素主要用于描述Java類自身的名稱和其父類名稱,接下來的字節碼流則開始描述類中的變量信息,這些變量既包括類的成員變量,也包括類變量(即靜態變量)信息。 首先看類成員變量a的信息

圖所選中的8字節,一共包含兩個常量池元素信息,這兩個常量池元素的類型都是字符串,因為其tag位都是1。第一個字符串常量的length是1,其值(即bytes)是0x61,正好對應UTF-8編碼的字符a。第二個字符串常量的length也是1,其值是0x49,正好對應UTF-8編碼的字符I。在JVM規范中,若變量的類型是I,則表示該變量的實際類型是int。這與上文對變量a的定義一致。 接著看類變量si的定義,

所選中的27字節,一共描述了兩個常量池元素,這兩個常量池元素的類型也都是字符串。第一個字符串的length為2,其值是0x7369,對應utf-8編碼的字符串si。第二個字符串的length為0x13,即19,其值是0x4C 6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 3E,這一串值是ASCII字符,每2個十六進制數對應一個ASCII字符,這些數字連起來就對應一個字符串,所對應的字符串是Ljava/lang/Integer;。 這兩個常量池元素合起來,描述了Test類中的static Integer si這樣的類變量。

總結

以上是生活随笔為你收集整理的class字节码文件中的常量池结构详解的全部內容,希望文章能夠幫你解決所遇到的問題。

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