日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

00 前言

JAVA程序具有 " Write Once , Run Anywhere ." 的跨平臺特性。實現這樣的目的,Java的方案是:半編譯 + 半解釋,即 .Class + JVM 。

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

1、源程序內容會被編譯為.Class文件,.Class文件具有嚴格規定如何從中提取信息,可以理解為 “中間碼”,約定使用者如何理解文件內容

2、理解了程序內容,各個平臺根據自身特色不同,實現各自的JVM用來解釋(翻譯).Class文件,變成真正的本地可執行指令。

如此實現了Java跨平臺的特性。因此,跨平臺的基礎為.Class,實現為JVM。

本文的目的為:讀懂.Class,悉知編寫的程序代碼在JVM眼中是什么樣子。而在理解了.Class之后,對于理解JVM、理解字節碼插樁等有進一步幫助。

01 基礎知識

字節碼

字節碼是一種包含執行程序,由數據對組成的二進制文件,是一種中間碼。一般來說,一字節占用8位,即包含八位的二進制。

文章所指的.Class文件為字節碼文件,每字節用16進制表示,數值范圍 00 ~ FF (0 ~ 255).

無符號數基本類型

無符號數可以用來描述數字、索引引用、數值量或按照 UTF-8 編碼構成字符串。u1、u2、u4、u8分別代表 1個字節、2個字節、4個字節和8個字節的無符號數。

字面量

字面量是一種固定值的表示法,本身沒有含義,需要場景來為它賦予含義,如何理解?比如 007 沒有含義,但是用來表示詹姆斯·邦德,你就知道007代表一個很厲害的特工。在程序中,int x = 10、String s = "10" 讓字面量 10 具有了不同的意義。

全限定名

將一個類的全限定名是將類全名的.全部替換為/,如java.lang.String替換為java/lang/String

描述符

描述符用來描述字段的數據類型、方法的參數類表和返回值,每種符號對應不同數據類型

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

02 Class文件

Java文件包含了一個類的所有信息,以下是一個Java類:

import java.io.Serializable;

public class TestClass implements Serializable{

private int m = 123;

private static int x = 10;

private static final int y = 20;

public int increace(){

return m+1;

}

public void m() throws Exception{

// 具體邏輯不寫

}

public static String hello(){

return "hello word";

}

}

此Java文件中,所包含的信息有:

類明為TestClass并可被外部訪問,實現了Serializable接口

擁有類變量 x和y,擁有成員變量 m

擁有可被外部訪問的類函數 hello(),擁有可被外部訪問的成員函數increace() 和 m()

note: 如無特殊說明,文章所說.Class文件均由此Java文件編譯得來這些信息在被編譯后將在.Class文件中進行表達。通過命令

Javac fileName.java

可將Java文件編譯成對應的.Class文件。.Class文件為字節碼文件,可借助對應編輯器閱讀。

本文使用的編輯器為 “010” ,windows 和 mac 都有, 自行下載。

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

Class 字節碼實例文件

.Class文件使用字節碼表達信息,各數據間緊湊,不包含任何分隔符,因此整個.Class文件中存儲的內容幾乎是全部程序運行時的必要數據。如何解析字節碼數據,就需要制定規則來解讀,嚴格遵守。

.Class 文件風格采用類似于C語言結構的偽結構來存儲數據。可以將.Class文件看成多張表的集合,通過表索引,能找到對應的數據。可以理解為,數據存在的相對位置,決定了它被賦予的涵義。

.Class文件格式如下表

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

有些數據信息是定長的,有些視具體情況而定,但都會有相應的約束告知具體長度。各信息對應已在.Class實例文件圖標出,剩下的是逐層去解析類信息。

03 常量池

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

類和接口的全限定名

字段的名稱和描述符

方法的名稱和描述符

與C和C++不同,Java代碼編譯后沒有“連接”的步驟,在JVM加載Class文件的時候進行動態連接。.Class文件不會保存各方法、字段的最終內存布局信息,因為不能經過運行期轉換無法得到真正的內存入庫地址,無法被JVM使用。在JVM運行時,從常量池中拿到對應的符號引用,解析、翻譯到具體的內存地址中再進行使用,這些信息也就存于JVM的方法區中。

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

常量池所占用長度不定,需要 0x0008 ~ 0x0009 提供常量數量統計,再根據常量池里的具體常量類型推算出具體總占用的長度。

但這比較繁瑣,每一種常量類型對應一份表,需要根據表的不同查閱具體的表結構來獲取信息。常量類型的第一位 u1 表明來對應常量的表結構,對應信息如下

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

除必要外,本文不打算列出各個對應的表結構,具體結構可參考

Class類文件結構之常量表

https://blog.csdn.net/u014296316/article/details/83020087

這里拋磚引玉。

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

常量池第一個常量

第一個常量類型由 0x000A 處標出,值為0A,十進制為10,查表,類型為CONSTANT_Methodref_info(不得不說現在的編輯器很強大,沒有對應功能的話就只能慢慢查了)。

表中數據類型為u1、u2、u2 共占 5 個字節(具體表信息和內容含義后文再續)。如果是CONSTANT_Utf8_info類型,還會有 length 屬性表明字面量占用字節長度,需要加上此長度。則第二個常量類型由 0x000F 處標出,值為0F,十進制為09,表類型為CONSTANT_Fieldref_info。以此類推...

這樣一步一步查找對應常量也是比較麻煩的,好在Java內置類工具——javap可對.Class文件字節碼進行分析,通過命令

javap -verbose fileName

能得到下圖信息 :(僅展示了常量池部分)

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

javap分析常量池

常量池數量值在 0x0008 ~ 0x0009 為 23,轉換十進制為35,表示常量池索引范圍為 1~35。觀察上兩張圖,前者索引從0開始,后者索引從1開始。

若摸不著門路,常量池的分析著實讓人頭大,個人看來,常量池里的信息是在 “搭積木” 。

本例子中,常量池涉及到的常量類型為:

CONSTANT_Methodref_info

CONSTANT_Fieldref_info

CONSTANT_String_info

CONSTANT_Class_info

CONSTANT_Interger

CONSTANT_NameAndType

CONSTANT_Utf8

暫時拋開具體表結構,以上表類型結構關系如示:

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

常量池常量類型結構

上面僅畫出了當前例子涉及到的常量類型的組成關系,任意類型的常量,不斷拆分,最后都會指向基本類型的常量CONSTANT_Utf8,或自身就為基本類型如CONSTANT_Interger。

可以理解為,基本常量類型CONSTANT_Utf8本身沒有過多意義,其它的類型為場景,為CONSTANT_Utf8賦予了意義。

CONSTANT_Utf8_info可以算是最基本的類型,結構為

// 偽代碼

{

// 常量類型

u1 tag;

// 字節長度

u2 length;

// UTF-8縮略編碼

bytes[length];

}

在遇到CONSTANT_Utf8_info類型的常量時,將bytes逐個按照UTF-8縮略編碼即可得到對應的字面量

04 類級信息

定義的類為

public class TestClass implements Serializable

其中包含的信息為:

類本身:TestClass

訪問標志:public

實現接口Serializable

父類為:Object

從.Class文件格式表中,在常量池后緊接著的數據,就是類級數據

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

從 0x0143 ~ 0x014C :

access_flags(u2): 十六進制值為 0x0021

this_class(u2): 十進制值為5,指向常量池第5個常量, 類型為CONSTANT_Class_info,類為 TestClass

super_class(u2): 十進制值為5,指向第6個常量,類型為CONSTANT_Class_info,類為 java/lang/Object

interface_count(u2): 實現接口數量 1 個

interface[0] :指向常量池第7個常量,類型為CONSTANT_Class_info,接口名誒 java/io/Serializable

CONSTANT_Class_info 常量表結構如下// 偽代碼

{

// 常量類型

u1 tag ;

// 指向常量池偏移量為name_index,類型為CONSTANT_Utf8_info類型的索引,

//代表類或接口的權限定名

u2 name_index;

}

與之前所說常量池里在搭積木的說法一致,后面涉及到的常量池里的類型依然如此。

訪問標志使用標志位來表示,各個標志含義如表

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

當前情況為 0x0001 | 0x0020 = 0x0021

05 attribute(屬性表)

屬性表比較特殊,.Class文件、字段表、方法表等都可以攜帶自己的屬性表集合,用來描述專有的場景,也因此將此表做前置說明。

屬性表的特點為:

規則較寬松,不要求嚴格的順序、長度、內容

只要不與已有屬性表重復,任何編譯器都可以向屬性表中寫入自定義的屬性信息,JVM會忽略掉不認識的屬性。

屬性表結構為// 偽代碼

{

// 指向常量池類型為CONSTANT_Utf8_info的常量,代表屬性名

u2 attribute_name_index ;

// 屬性表info占用長度

u4 attribute_length;

// 這需要具體實現的結構,長度為attribute_length

Info info;

}

因此一個屬性表的長度為 u2 + u4 + attribute_length。Java與定義來很多屬性表,文章檢出涉及到的做后續說明,其它在實際需要時自行查閱

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

字段表與方法表攜帶的屬性表暫未涉及,當前節點涉及到類型為SourceFile的.Class攜帶的屬性表。

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

class攜帶屬性表示例

范圍為 0x025A ~ 0x0262 共占 u2 + u4 + attibute_length = 8 字節,SourceFile屬性結構如下

偽代碼

{

// 指向常量池類型為CONSTANT_Utf8_info的常量,代表屬性名

u2 attribute_name_index ;

// 屬性表內容占用長度

u4 attribute_length;

// 指向常量池類型為CONSTANT_Utf8_info的常量,代表源文件名

u2 sourcefile_index;

}

因此通過此SourceFile屬性表,得知源文件名為TestClass.java

06 字段表

查閱.Class文件格式表,接口表之后,就是字段數量已經字段數量表

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

字節碼字段表

從 0x014D ~ 0x016E , 其中 0x014D ~ 0x014E 表示字段數, 值為 0x0003,表示字段數為3,隨便就是緊挨著的字段表。字段表結構如下

// 偽代碼

{

// 訪問標志

u2 access_flags

// 指向常量池類型為CONSTANT_Utf8_info的常量,表示字段名

u2 name_index

// 指向常量池類型為CONSTANT_Utf8_info的常量,用描述符表示

// 字段類型

u2 descriptor_index

// 屬性表數量

attributes_count

// 屬性表內容

attribuite_info

}

字段表和.Class一樣,能攜帶自己的屬性表處理特殊場景,attribuite_info是非必須的。當attributes_count的值為0時, 說明無需attribuite_info。字段也擁有訪問標志來對字段做進一步約束。字段名則用name_index指向常量池的常量來表示,字段類型則用描述符來表示, 比如 Int 表示為 I (忘了往前看基礎知識)。

當前例子定義的字段如下:

private int m = 123;

private static int x = 10;

private static final int y = 20;

定義了成員變量 m 和 類變量 x ,y。舉例 y 來看

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

位置為 0x015F ~ 0x016E,其中:

訪問標志:0x001A

字段名索引:0x000B,十進制值為11,指向常量池第11個常量,為y

描述符索引為:0x0009,指向常量池第9個常量,為I

屬性表數為:0x0001,數量為1

屬性表總占用長度為:u2 + u4 + 2字節 共10位,即 0x0167 ~ 0x016E

字段訪問標志位含義如表:

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

當前為 private static final ,即 0x0001 | 0x0008 | 0x0010 , 為 0x001A。

通過訪問標志、字段索引、描述符信息,就可以拿到

-> private static final int y 這一信息。

對于使用 final和static修飾的并且時基本數據類型的變量,會使用屬性表ConstantVulue來進行賦值。屬性表 ConstantVulue 除了約定的基本數據外,還有類型為 u2 的ConstantValue_index 索引來表示指向常量池中的常量用來初始化數據。值位于 0x016D ~ 0x016E,為 0x000D,指向的常量表索引13處的值為整型的20;

而實例中的變量m,類變量x則在成員初始函數、類初始函數中進行賦值,下文做說明。

07 方法表

字段表之后,緊挨著的是方法數量與方法表集合

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

范圍為 0x016F ~ 0x0258,方法數值為 0x0005 共 5 個方法。除了示例自定義的 increace() ,m() 和 hello() 外,還有實例構造方法<init>()v,類構造器<clinit>()方法。

方法表結構為:

偽代碼

{

// 訪問標志

u2 access_flags;

// 方法名索引,指向常量池類型為CONSTANT_Utf8_info的常量

u2 name_index;

// 方法返回值描述符索引,指向常量池類型為CONSTANT_Utf8_info的常量

u2 descriptor_index;

// 屬性表數量

u2 attributes_count;

// 屬性表內容

Info info;

}

方法也可以攜帶屬性表來描述專有場景。通過上述結構以及具體字節碼,可以推算出方法所包含的內容。其中,訪問標志含義如表:

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

取構造實例方法方法<init>()做示例來看:

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

信息為:

access_flags: 0x0001 ,為public

name_index: 0x000E,為14,對應常量池得到 <init>

descriptor_index: 0x000F,為15,對應常量池得到 ()V

attributes_count: 0x0001,為1,屬性表數量為1

可以根據信息反推得到函數信息,實例化函數被表達為 public <init>()V,依次為訪問標志位、函數名、返回值。

一個函數方法,更重要的如何表述它所提供的功能。本質上來說,函數體里所有的代碼段都是在進行運算操作,因此,只要將函數體里的代碼段轉換為字節碼指令即可,函數執行時根據執行即可,再次之上再幾率一下關鍵信息,就能得出函數執行、棧深度、局部變量數、字節碼占用文件大小。

在attributes_count之后,從 0x0179 ~ 0x01A5 是屬性表包含的內容。根據之前所說的屬性表約定的格式 0x0179 ~ 0x0180 位置值為 0x0010,十進制為16,查找啊常量池知屬性表為Code類型。Code類型屬性表結構如下:

// 偽代碼

{

// 屬性表名稱索引,指向常量表類型為CONSTANT_Utf8_info的常量

u2 attribute_name_index;

// 屬性表長度

u4 attribute_length;

// 棧深

u2 max_stack;

// 局部變量數

u2 max_locals;

// 字節碼指令長度

u4 code_length;

// 字節碼指令

u1 code code_length;

// 異常表數量

u2 exception_table_length;

// 異常表

exception_info exception_table;

// 屬性表數量

u2 attributes_count;

// 屬性表

attribute_info attributes;

}

獲取方法信息不僅能直接通過閱讀字節碼文件,通過javap -verbose className也可以拿到,一起貼了

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

紅圈為字節碼指令集,黃圈為每一條字節碼指令,綠圈為Code屬性表的基本信息,藍圈為javap工具解析出的實例函數信息。

首先,最大棧深為2,在函數執行的任意時刻都不會超過這個操作數棧深度的最大值;然后局部變量數為1,表示局部變量表所需的存儲空間,單位為Slot,此單位是JVM為局部變量分配內存所使用的最小單位。藍圈里還有args_sige表示方法接受的參數數,這里為1,也就是 this。最后就是函數代碼塊里轉成的字節碼指令里。

字節碼指令不在文章的討論范圍內,不妨簡單了解。

字節碼指令代表著某種特定操作,由一個字節長度代表其操作含義,后面可以跟隨0到多個所需操作數。

本例子中的初始化函數被翻譯成了:

2A B7 00 01 2A 10 7B B5 00 02 B1

也就是上圖藍圈處的:

{

// 將 this 入棧

0: aload_0

// 喚醒父類實例化函數

1: invokespecial #1

4: aload_0

// 將 123 入棧

5: bitpush 123

// 訪問字段 m,將123存入

7: putfield #2

// 方法返回

10: return

}

這里只說明 putfield #2,對應的字節碼為 B5 00 02,其中 B5 代表操作執行 00 02 為操作所需參數,值為 0x0002,表示指向常量池類型為 CONSTANT_Fieldref_info 的常量。CONSTANT_Fieldref_info 結構為

// 偽代碼

CONSTANT_Fieldref_info

{

u1 tag;

// 指向常量池類型為CONSTANT_ClassInfo_info的常量

// 代表字段所屬類

u2 class_index;

// 指向常量池類型為CONSTANT_NameAndType_info的常量

// 代表字段名和類型

u2 name_and_type_index;

}

CONSTANT_ClassInfo_info

{

u1 tag;

// 指向常量池尾CONSTANT_Utf8_info的常量

// 代表類名

u2 name_index;

}

CONSTANT_NameAndType_info

{

u1 tag;

// 指向常量池尾CONSTANT_Utf8_info的常量

// 代表 名稱

u2 name_index;

// 指向常量池尾CONSTANT_Utf8_info的常量

// 代表 所屬類型

u2 descriptor_index;

}

當前為指向第二個常量,對照信息:

在 JVM 眼中 .class 文件是什么樣的?值得收藏

 

m初始化

能知道字節碼操作 B5 00 02 是將棧中的 123 給 TestClass.m 進行賦值 ,也就是例子中定義的 private int m = 123 的賦值操作。

而 x 的賦值則在類構造器<cinit>中進行,inicrea()和m()函數也可以用相同的方式進行分析。不再陳述,點到為止。

08 總結

至此,了解.Class文件的如何,通過.Class文件格式表可以解析出文件內容的基本信息。.Class文件可以看成多張表的集合,根據表制定的規則,順藤摸瓜,自然能找出對應信息。

.Class文件在如何表達信息上不難理解,難的是有耐心去縷清這些瑣碎的索引關系,尤其常量池和屬性表部分。屬性表部分則提供了足夠的發揮空間,根據場景提供更多內容。

文章僅了解了解析.Class文件的基本規則,更進一步的解析規則感興趣或需要時再了解即可,方法不變。

參考

《深入理解Java虛擬機》 —— 第6章

1、深入理解JVM字節碼執行引擎https://blog.csdn.net/suifeng629/article/details/823497842、java中class文件的意義是什么?https://blog.csdn.net/dangbai01_/article/details/800973403、java語言為什么可以跨平臺https://blog.csdn.net/banjing_1993/article/details/82349013

歡迎在留言區留下你的觀點,一起討論提高。如果今天的文章讓你有新的啟發,學習能力的提升上有新的認識,歡迎轉發分享給更多人。

分享到:
標簽:JVM class
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定