JVM 之 class文件詳解

目錄

一. 前言

二. class文件結構

2.1. 文件格式

2.2. 魔數與版本號

2.3. 常量池

2.4. 訪問標志

2.5.?類索引、父類索引和接口索引集合

2.6. 字段表集合

2.7.?方法表集合

2.8. 屬性表集合

2.8.1. Code 屬性表

2.8.2.?Exceptions 屬性

2.8.3.?LineNumberTable 屬性

2.8.4.?LocalVariableTable 屬性

2.8.5.?SourceFile 屬性

2.8.6.?ConstantValue 屬性

2.8.7.?InnerClass 屬性

2.8.8.?Deprecated 屬性和 Synthetic 屬性

2.8.9.?StackMapTable 屬性

2.8.10.?Signature 屬性

2.8.11.?BootstrapMethods 屬性

三. 實例分析

3.1. 示例源碼

3.2. 魔數

3.2. 版本號

3.3. 常量池

3.3.1. 常量池第 1 個常量

3.3.2. 常量池第 2 個常量

3.3.3.?常量池中第 3~4 常量

3.3.4.?常量池中第 5~14 及 17、18常量

3.3.5. 常量池第 15、16 個常量

3.4. 訪問標志

3.5. 類索引、父類索引

3.6. 字段表集合

3.7. 方法表集合

3.7.1.?第 1 個方法表

3.7.2. 第 2 個方法表

3.8.?Code 屬性表與 LineNumberTable 屬性表

3.9. SourceFile 屬性


一. 前言

? ? 我們平時在DOS界面中往往需要先運行 javac 命令,這個命令的直接結果就是產生相應的class文件,然后基于這個class文件才可以真正運行程序得到結果。當然,這是Java虛擬機的功勞,那么是不是Java虛擬機只能編譯 .java 的源文件呢?答案是否定的。時至今日,Java虛擬機已經實現了語言無關性的特點。而實現語言無關性的基礎是虛擬機和字節碼的存儲格式,Java虛擬機已經不和包括Java語言在內的任何語言綁定。它只與“class”文件這種特定的二進制文件相關聯。在class文件中包含了Java虛擬機指令集和符號表以及若干輔助信息。可以很容易想到Java(本質上不是Java語言本身的平臺無關性,而是其底層的Java虛擬機的平臺無關性使然。)的跨平臺,因為任何一門功能性語言都可以表示為能被Java虛擬機接受的有效的class文件。比如,除了Java虛擬機可以將Java源文件直接編譯為class文件外,使用JRuby等其他語言的編譯器一樣可以把程序代碼編譯成class文件,由此可見,Java虛擬機并不關心class文件是由何種語言編譯來的。

二. class文件結構

下面的圖是一字排開形式,都是平級關系,由于圖片排開太長,所以分行顯示,如下面這樣:

class文件格式采用一種類似C語言結構體的偽結構來存儲數據,這種偽結構只有兩種數據結構,即無符號數和表,解析class文件全是以這兩個數據結構為基礎。
無符號數:屬于基本的數據類型,由1字節、2字節、4字節、8字節分別用u1、u2、u3、u8表示,可以用來描述數字、索引引用、數量值或者UTF-8編碼構成字符串值。
表:是由多個無符號數或者表構成的復合數據結構,習慣以“_info”結尾class文件本質也可以看作一張表。

2.1. 文件格式

class文件格式嚴格按照下表的方式進行排列構成:

類型名稱含義數量
u4magic魔數1
u2minor_version副版本號1
u2major_version主版本號1
u2constant_pool_count常量池計數器1
cp_infoconstant_pool常量池數據區(常量池表)constant_pool_count -1
u2access_flags訪問標志(類的訪問控制權限)1
u2this_class類索引1
u2super_class父類索引1
u2interfaces_count接口計數器1
u2interfaces接口信息數據區(接口表)interfaces_count
u2fields_count字段計數器1
field_infofields字段信息數據區(字段表)fields_count
u2methods_count方法計數器1
method_infomethods方法信息數據區(方法表)methods_count
u2attributes_count附加屬性計數器1
attribute_infoattributes附加屬性信息數據區(附加屬性表)attributes_count

2.2. 魔數與版本號

? ? 魔數固定為 0xCAFEBABE,4個字節。魔數的唯一作用在于確定這個class文件是否是Java虛擬機接受的class文件。如gif和jpeg等在文件頭中都存在魔術,使用魔術而不是使用擴展名是基于安全性考慮的——擴展名可以隨意被改變。

? ??緊接著魔數的4個字節是class文件版本號:版本號又分為副版本號主版本號。其中前兩個字節用于表示副版本號,后兩個字節用于表示主版本號。版本號是隨著JDK版本的不同而表示不同的版本范圍的。如果class文件的版本號超過虛擬機版本,將被拒絕執行。

2.3. 常量池

? ? 在版本號之后,則是常量池入口,常量池在class文件中的作用非常重要,就是class文件中的資源倉庫,通常也是占用class文件空間最大的數據項目之一。由于常量池長度不是一定的,所以在常量池的入口處放置了一個兩個字節的常量池計數器來記錄常量池的容量有多大。

常量池計數器:記錄的是常量池數據區中的常量池項 cp_info 的數量。
1. 從1開始計數,第一個有用常量池項為1,所以常量池項的索引也是從1開始;
2. 至于索引為0的數據空間,class文件規范是如此定義的。

在常量池中主要存放字面量符號引用。字面量比較接近Java語言層面的常量概念,比如文本字符串、聲明為final的常量值等。符號引用則主要包括三類常量:
1. 類和接口的全限定名;
2. 字段的名稱和描述符;
3. 方法的名稱和描述符。

// 常量池項 (cp_info) 的結構cp_info{tag:xxinfo[]:xx
}

tag:類型,用標記下面info數組的數據類型。
info[]:若干個字節構成數組。

常量常量說明項目類型描述
CONSTANT_Utf8_info

表示字符串常量的值。

(字面量型結構體)

tag標志u1值為1
lengthu2UTF-8編碼的字符串占用的字節數
bytesu1長度為length的UTF-8編碼的字符串
CONSTANT_Integer_info表示4字節(int)的數值常量。

(字面量型結構體)

tag標志u1值為3
bytesu4按照高位在前存儲的int值
CONSTANT_Float_info表示4字節(float)的數值常量。

(字面量型結構體)

tag標志u1值為4
bytesu4按照高位在前存儲的float值
CONSTANT_Long_info表示8字節(long)的數值常量。

(字面量型結構體)

tag標志u1值為5
bytesu8按照高位在前存儲的long值
CONSTANT_Double_info表示8字節(double)的數值常量。

(字面量型結構體)

tag標志u1值為6
bytesu8按照高位在前存儲的double值
CONSTANT_Class_info

表示類或接口的完全限定名。

(引用型結構體)

tag標志u1值為7
indexu2指向全限定名常量項的索引
CONSTANT_String_info表示java.lang.String類型的常量對象。

(引用型結構體)

tag標志u1值為8
indexu2指向字符串字面量的索引
CONSTANT_Fieldref_info

表示類中的字段。

(引用型結構體)

tag標志u1值為9
indexu2指向聲明字段的類或接口描述符CONSTANT_Class_info的索引項
indexu2指向字段描述符CONSTANT_NameAndType的索引項
CONSTANT_Methodref_info

表示類中的方法。

(引用型結構體)

tag標志u1值為10
indexu2指向聲明方法的類描述符CONSTANT_Class_info的索引項
indexu2指向名稱及類型描述符CONSTANT_NameAndType的索引項

CONSTANT_InterfaceMethodref

_info

表示類中的方法。

(引用型結構體)

tag標志u1值為11
indexu2指向聲明方法的接口描述符CONSTANT_Class_info的索引項
indexu2指向名稱及類型描述符CONSTANT_NameAndType的索引項

CONSTANT_NameAndType_info

表示字段或方法的名稱和類型。

(引用型結構體)

tag標志u1值為12
indexu2指向該字段或方法名稱常量項的索引
indexu2指向該字段或方法描述符常量項的索引
CONSTANT_MethodHandle_info

表示方法句柄。

(引用型結構體)

tag標志u1值為15
CONSTANT_MethodType_info

表示方法類型。

(引用型結構體)

tag標志u1值為16
CONSTANT_Dynamic_info

表示一個動態計算常量。

(引用型結構體)

tag標志u1值為17

CONSTANT_InvokeDynamic

_info

用于表示invokedynamic指令所使用到的引導方法(Bootstrap Method)、引導方法使用到動態調用名稱(Dynamic Invocation Name)、參數和請求返回類型tag標志u1值為18
CONSTANT_Module_info表示一個模塊tag標志u1值為19
CONSTANT_Package_info表示一個模塊中開放或者導出的包tag標志u1值為20

細化了的常量池的結構會是類似下圖所示的樣子:

在JVM規范中,每個字段或者變量都有描述信息,描述信息的主要作用是 數據類型、方法參數列表、返回值類型等。基本參數類型和void類型都是用一個大寫的字符來表示,對象類型是通過一個大寫L加全類名表示,這么做的好處就是在保證JVM能讀懂class文件的情況下盡量的壓縮class文件體積。

基本數據類型表示:

字符類型字符類型
BbyteCchar
DdoubleFfloat
IintJlong
SshortZboolean
Vvoid

對象類型:前面加一個L,然后把.改為/,String------>Ljava/lang/String;(后面有一個分號)。
數組類型:每一個維度都是用一個前置 [ 來表示,比如: int[] ------>[ I,String [][]------>[[Ljava.lang.String;(后面有一個分號)。
用描述符來描述方法:先參數列表,后返回值的格式,參數列表按照嚴格的順序放在()中
比如源碼 String getUserByIdAndName(int id, String name) 的方法描述符號為(I,Ljava/lang/String;)Ljava/lang/String;(后面有一個分號)。

2.4. 訪問標志

? ? 常量池之后的數據結構是訪問標志(access_flags),這個標志主要用于識別一些類或者接口層次的訪問信息,主要包括:這個Class是類還是接口、是否定義public、是否定義abstract類型;如果是類的話是否被聲明為final等。

類的標志訪問如下:

標志名稱標志值含義
ACC_PUBLIC0x0001是否為public
ACC_FINAL0x0010是否被聲明為final,只有類能設置
ACC_SUPER0x0020是否允許使用invokespecial字節碼指令的新語義
ACC_INTERFACE0x0200標識這是一個接口
ACC_ABSTRACT0x0400是否為abstract類型,對于接口或者抽象類來說,此標志位為真,其他類型為假
ACC_SYNTHETIC0x1000是否為這個類是由編譯器自動產生,并非由用戶代碼產生的
ACC_ANNOTATION0x2000標識這是一個注解
ACC_ENUM0x4000標識這是一個枚舉類
ACC_MODULE0x8000標識這是一個模塊

字段的標志訪問如下:

方法的標志訪問如下:?

兩個訪問修飾符直接通過位運算來實現的。例如:現在有一個class文件訪問標識為00 21。那么實際它代表的是0x0021 = 0x0020 位運算 0x0001,即它代表這個class的訪問權限是ACC_PUBLIC和ACC_SUPER。

2.5.?類索引、父類索引和接口索引集合

? ? 類索引和父類索引都是一個兩個字節的數據,而接口索引是一組兩字節的數據集合,class文件中由這三項數據來確定該類型的繼承關系,類索引用于確定該類的全限定類名,父類索引用于確定該類父類的全限定類名。由于Java語言特性不支持多類繼承,所以父類索引只有一個,接口索引集合用于描述這類實現了哪些接口。

2.6. 字段表集合

? ? 字段表用于描述接口或者類中聲明的變量。字段包括類級變量實例級變量,但是不包括方法內部聲明的局部變量(這些變量是存儲在Java虛擬機棧中的局部變量表中的)。自然,描述一個字段的信息包括:字段的作用域(public、protected、private)、實例變量與否(static)、可變性(final)、并發可見性(volatile)、可否被序列化(transient)、字段數據類型(基本數據類型、對象、數組)、字段名稱。

字段的信息也被存放在一張表中,其字段表包括三種類型:
1. u2類型訪問標志(access_flags),其訪問標志在access_flags中。
2. u2類型的name_index(字段的簡單名稱)。
3. u2類型的描述符(descriptor_index)。

字段表格式:

類型名稱數量
u2access_flags1
u2name_index1
u2descriptor_index1
u2attributes_count1
attribute_infoattributesattributes_count

2.7.?方法表集合

? ? JVM中堆方法表的描述與字段表是一致的,包括了:訪問標志、名稱索引、描述符索引、屬性表集合。方法表的結構與字段表是一致的,區別在于訪問標志的不同。在方法中不能用volatile和transient關鍵字修飾,所以這兩個標志不能用在方法表中。在方法中添加了字段不能使用的訪問標志,比如方法可以使用synchronized、native、strictfp、abstract關鍵字修飾,所以在方法表中就增加了相應的訪問標志。

注意:如果父類方法沒有在子類中重寫,那么在方法中不會自動出現來自父類的方法信息。同樣的,有可能添加編譯器自動增加的方法,比如方法。

方法表的結構如下:

2.8. 屬性表集合

? ? 前面的class文件、字段表和方法表都可以攜帶自己的屬性信息,這個信息用屬性表進行描述,用于描述某些場景專有的信息。在屬性表中沒有類似class文件的數據項目類型和順序的嚴格要求,只要新的屬性不與現有的屬性名重復,任何人都可以向屬性表中寫入自己定義的屬性信息。?

2.8.1. Code 屬性表

? ? Java程序方法體中的代碼經過 javac 編譯最終編譯成的字節碼指令就保存在Code屬性中。但是并非所有的方法表都必須存在這個屬性。Code屬性是class文件中最重要的一個屬性,如果把一個Java程序中的信息分為代碼(Code)和元數據(Metadata,包括類、字段、方法定義及其其他信息)兩部分,那么在整個class文件中,Code屬性用于描述代碼,所有其他的數據項目都用于描述元數據。

Code屬性表的結構:

類型名稱數量說明
u2attribute_name_index1指向CONSTANT_Utf8_info型常量的索引,此常量固定為“Code”,代表該屬性的屬性名稱
u4attribute_length1表示屬性長度
u2max_stack1表示操作數棧深度的最大值
u2max_locals1表示局部變量表所需的存儲空間,單位是變量槽(虛擬機為局部變量分配內存所使用的最小單位)
u1code_length1表示字節碼長度
u2codecode_length用于存儲字節碼指令。用來存儲java源文件編譯后生成的字節碼指令
u2exception_table_length1表示異常處理表集合長度
exception_infoexception_tableexception_table_length表示異常處理表集合,異常處理表并不是Code必須部分!
u2attributes_count1
attribute_infobattributesattribute_count

2.8.2.?Exceptions 屬性

? ? Exceptions 屬性的作用是列舉出方法中可能拋出的受檢異常(Checked Exception),也就是描述throws 后的列舉的異常。

2.8.3.?LineNumberTable 屬性

? ? LineNumberTable屬性用于描述Java源碼行號與上傳字節碼行號之間的對應關系,并不是運行的必須屬性,但會默認生成到class文件中,主要作用就是拋出異常時會顯示行號,以及調試程序時可以根據源碼行號進行設置斷點。

LineNumberTable的屬性結構如下:

類型名稱數量
u2attribute_name_index1
u4attribute_length1
u2line_number_table_length1
line_number_infoline_number_tableline_number_table_length

line_number_table是一個 l類型為line_number_info的集合,包含start_pc和line_number兩個屬性都是占兩個字節,前者是字節碼行號,后者是java源碼行號。?

2.8.4.?LocalVariableTable 屬性

? ? 用于描述棧幀中局部變量表中的變量與Java源碼中定義的變量的之間的關系。也不是必須的屬性。如果沒有這個屬性,產生的直接影響就是當別人引用這個方法的時候,所有的參數名稱都會丟失,IDE將會使用諸如 args0、args1 之類的參數進行顯示。自然,當調試程序的時候,顯示的參數名稱是不可知的。

2.8.5.?SourceFile 屬性

? ? 用于記錄這個class文件的源碼文件名稱,當類名和文件名不一致時拋出異常。如果不使用這個屬性,那么當拋出異常的時候,堆棧中將不會顯示出錯代碼所屬的文件名。

SourceFile屬性的結構為:

類型名稱數量
u2attribute_name_index1
u4attribute_length1
u2sourcefile_index1

2.8.6.?ConstantValue 屬性

? ? ConstantValue 屬性作用是通知虛擬機自動為靜態變量賦值。要注意的是,只有被 static 關鍵字修飾的變量才可以使用這個屬性(類變量)。對于非類變量,初始化是在方法中進行的;對于類變量可以選擇兩種方式進行變量的初始化:一是在類構造器方法中使用;二是是ConstantValue屬性。目前Sun HotSpot的選擇原則是:如果一個變量同時使用static和final關鍵字修飾,并且這個變量是基本數據類型或者 java.lang.String 類型的話,就使用ConstantValue屬性進行初始化。如果沒有被 final 修飾或者并非是基本數據類型,那么將會選擇使用方法進行初始化。

2.8.7.?InnerClass 屬性

這個屬性主要用于記錄內部類與宿主類之間的關聯關系。

2.8.8.?Deprecated 屬性和 Synthetic 屬性

這兩個屬性都屬于標志類型的布爾屬性,只存在有沒有的區別。

Deprecated 屬性用于表示某個類、字段或者方法,已經被程序作者定為不再推薦使用,可以通過注解 @deprecated 實現。

Synthetic 屬性代表此字段并不是由Java源碼產生的,而是通過編譯器自行添加的。

2.8.9.?StackMapTable 屬性

該屬性的目的在于代替以前比較消耗性能的基于數據流分析的類型推導驗證器。

2.8.10.?Signature 屬性

? ? 這個屬性是專門用來記錄泛型類型的,因為在Java語言采用的是擦除法實現的泛型,在字節碼(Code屬性)中,泛型信息編譯之后會被擦除。擦除法的優點是能夠節省泛型所占的內存空間,缺點是在運行期間無法通過反射得到泛型信息,而Signature屬性則彌補了這一缺陷。現在的Java反射API已經能夠得到泛型信息,功勞就在于這個屬性。

2.8.11.?BootstrapMethods 屬性

? ? 這個屬性用于保存 invokedynamic 指令引用的引導方法限定符。該指令用于在運行時動態解析出調用點限定符所引用的方法,并執行該方法。

三. 實例分析

3.1. 示例源碼

示例代碼:

public class Test {private int m;public int inc(){return m + 1;}
}

編譯后的 class文件(使用16進制的方式來打開):

cafe babe 0000 0034 0013 0a00 0400 0f09
0003 0010 0700 1107 0012 0100 016d 0100
0149 0100 063c 696e 6974 3e01 0003 2829
5601 0004 436f 6465 0100 0f4c 696e 654e
756d 6265 7254 6162 6c65 0100 0369 6e63
0100 0328 2949 0100 0a53 6f75 7263 6546
696c 6501 0009 5465 7374 2e6a 6176 610c
0007 0008 0c00 0500 0601 0004 5465 7374
0100 106a 6176 612f 6c61 6e67 2f4f 626a
6563 7400 2100 0300 0400 0000 0100 0200
0500 0600 0000 0200 0100 0700 0800 0100
0900 0000 1d00 0100 0100 0000 052a b700
01b1 0000 0001 000a 0000 0006 0001 0000
0001 0001 000b 000c 0001 0009 0000 001f
0002 0001 0000 0007 2ab4 0002 0460 ac00
0000 0100 0a00 0000 0600 0100 0000 0400
0100 0d00 0000 0200 0e

3.2. 魔數

ca fe ba be // 魔數值0xCAFEBABE

上述代碼可以看出 class文件的魔術值為0xCAFEBABE。

? ? MagicNumber?為每個class文件的頭4個字節,唯一作用就是用于判斷這個文件是否為一個能被虛擬機接受的 class文件。為什么使用魔數不使用擴展名進行判斷?(出于安全考慮,擴展名可以隨意修改,魔術值只要沒有被廣泛采用就不會引起混淆)

3.2. 版本號

00 00 00 34 // 前兩位為次版本號,后兩位為主版本號

? ? 版本號為魔數后4個字節,前兩個字節為副版本號,后兩個字節為主版本號,0034十進制是52表示Java8,關于副版本號,在JDK1.2時短暫使用過,從JDK1.2到JDK12之前副版本號均為零,未被使用過。

3.3. 常量池

00 13       // 00 13表示常量中常量的數量為18 

3.3.1. 常量池第 1 個常量

觀察例子中常量池第一個常量,它的標志位為0x0a,根據2.3 章節表可得知這個常量類型為CONSTANT_Methoddref_info,CONSTANT_Methoddref_info 類型結構為:

類型名稱數量描述
u1tag1值為10
u2index1指向方法的類描述符CONSTANT_Class_info索引項
u2index1指向名稱及類型描述符CONTANT_Name_AndType索引項

結合字節碼分析,該類型一共占5個字節:
第一個占1個字節,用于區分索引類型;
第二個占2個字節,用于指向該類型中的CONSTANT_Class_info在常量池中的索引;
第三個占2個字節,用于指向該類型中的CONSTANT_NameAndType在常量池中的索引。

0a			// 0a表示第 1 個常量類型為CONSTANT_Methoddref_info 長度為5個字節(包含標志位)
00 04       // 表示聲明方法的類的字段 CONSTANT_Class_info的索引項在常量池第4項常量 
00 0f       // 表示名稱及類型字段 CONSTANT_NameAndType的索引項在常量池的第 15 項常量

3.3.2. 常量池第 2 個常量

例子中常量池第二個常量,它的標志位為0x09,根據2.3 章節表可得知常量類型CONSTANT_Fieldref_info 的類型結構為:

類型名稱數量描述
u1tag1值為9
u2index1指向方法的類描述符CONSTANT_Class_info索引項
u2index1指向名稱及類型描述符CONTANT_Name_AndType索引項

結合字節碼分析,該類型一共占5個字節:
第一個占1個字節,用于區分索引類型;
第二個占2個字節,用于指向該類型中的CONSTANT_Class_info在常量池中的索引;
第三個占2個字節,用于指向該類型中的CONSTANT_NameAndType在常量池中的索引。

09			// 表示第 2 個常量類型為 CONSTANT_Fieldref_info 長度為5個字節(包含標志位)
00 03 		// 表示聲明方法的類的字段 CONSTANT_Class_info 的索引項在常量池第 3 項常量 
00 10 		// 表示字段CONSTANT_Name-AndType的索引在常量池的第 16 項

3.3.3.?常量池中第 3~4 常量

例子中第3個和第4個常量類型一樣,它的標志位都為0x07,根據2.3 章節表可得知常量類型為CONTANT_Class_info,類型結構為:

類型名稱數量描述
u1tag1值為7
u2index1指向全限定名常量的索引

結合字節碼分析,該類型一共占3個字節:
第一個占1個字節,用于區分索引類型;
第二個占2個字節,用于指向全限定名常量項的索引。

07			// 表示第 3 個常量類型為 CONTANT_Class_info 長度為 3 個字節(包含標志位)
00 11       // 表示全限定名存在常量池的第 17 項常量
07    		// 表示第 4 個常量類型為 CONTANT_Class_info 長度為 3 個字節(包含標志位)
00 12 		// 表示全限定名存在常量池的第 18 項常量

3.3.4.?常量池中第 5~14 及 17、18常量

第5到14以及17、18常量,它們標志位都是0x01,根據2.3 章節表可得知常量類型為CONTANT_Utf8_info,?類型結構為 CONTANT_Class_info,類型結構為:

類型名稱數量描述
u1tag1值為1
u2length1UTF-8編碼的字符串占用了字節數
u1byteslength長度為length的UTF-8編碼的字符串

結合字節碼分析:
第一個占1個字節,用于區分索引類型;
第二個占2個字節,用于描述該常量占用多少(length)字節數;
第三個占length個字節數,用于存儲utf-8編碼的字符串?。

01 			// 表示第 5 個常量類型為 CONTANT_Utf8_info 長度為 4 個字節(包含標志位)
00 01 		// 表示utf-8編碼占用了字節數為 1
6d    		// 6d 十進制為109 utf-8對應 m
01			// 表示第 6 個常量類型為 CONTANT_Utf8_info
00 01 		// 表示長度為 1
49 			// 49 十進制為 73 utf8對應 I
01			// 第 7 個常量為CONTANT_Utf8_info
00 06		// 常量長度為 6
3c 69 6e 69 74 3e // 一一對應 '<init>'
<!-- 下面utf全部簡略的描述 -->
01 			// 第 8 個常量
00 03 		// 長度為 3
28 29 56	// utf對應 ()V
01 			// 第 9 個常量
00 04 		// 長度為 4
43 6f 64 65 // Code
01 			// 第 10 個變量
00 0f		// 長度為 15
4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 // LineNumberTable
01 			// 第 11 個變量
00 03		// 長度為3
69 6e 63	// inc
01			// 第 12 個變量
00 03		// 長度為3
28 29 49	// ()I
01  		// 第 13 個變量
00 0a		// 長度為10
53 6f 75 72 63 65 46 69 6c 65  // SourceFile
01 			// 第 14 個變量
00 09 		// 長度為9
54 65 73 74 2e 6a 61 76 61 // Test.java
······
01 			// 第 17 個常量為 utf-8
00 04		// 長度為 4
54 65 73 74 // Test
01			// 第 18 個常量為 utf-8
00 10		// 長度為 16
6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74  // java/lang/Object

3.3.5. 常量池第 15、16 個常量

第15、16個常量,它們的標志為都是0x0c,根據2.3 章節表可得知常量類型為CONSTANT_NameAndType_info,類型結構如下:

類型名稱數量描述
u1tag1值為12
u2index1指向該字段或方法名稱常量項索引
u2index1指向指向該字段或方法描述常量項索引

結合字節碼分析,該類型一共占5個字節:
第一個占1個字節,用于區分索引類型;
第二個占2個字節,指向該字段或方法名稱常量項索引;
第三個占2個字節,指向指向該字段或方法描述常量項索引。

0c 			// 表示第 15 個常量量類型為 CONSTANT_NameAndType_info 長度為5個字節(包含標志位)
00 07 		// 表示該字段的表示的字段或方法的名稱為常量池的第7個常量
00 08 		// 表示該字段表示的字段或方法的描述為位常量池的第8個常量
0c			// 表示第 16 個常量量類型為 CONSTANT_NameAndType_info 長度為5個字節(包含標志位)
00 05		// 表示該字段的表示的字段或方法的名稱為常量池的第5個常量
00 06		// 表示該字段表示的字段或方法的描述為位常量池的第6個常量

到這常量池已經全部解析完成:?可以使用 javap -verbose 命令輸出常量表,對常量表進行驗證:

3.4. 訪問標志

0021 根據2.3 章節表可得知該類使用 invokespecial 字節碼指令的新語義,且被public修飾。

00 21		// access_flags 標志位占用 2個字節位,0021表示被public修飾符修飾,且使用invokespecial字節碼指令的新語義

3.5. 類索引、父類索引

00 03 		// this_class 類索引,占用 2 個字節,表示在類名稱存放在常量池第 3 項常量中
00 04 		// super_class 父類索引,占用 2 個字節,表示父類名稱存放在常量池第 4 項常量中
00 			// interface_count 占用 1 個字節,表示實現接口的數量為0
00			// interfaces 接口為空

3.6. 字段表集合

<!-- 字段表集合 -->
00 01 		// fields_count 表示只有 1 個字段表數據 占用 2 個字節
00 02		// access_flags 表示字段修飾符為private 占用 2 個字節
00 05 		// name_index 表示字段名稱存放在常量池第 5 項常量 占用 2 個字節
00 06		// descriptor_index 表示字段方法的描述存放在常量池的第 6 項常量 占用 2 個字節
00 			// attributes_count 屬性表數量為0 占用 2 個字節
00 			// attributes 表示屬性表集合為空

3.7. 方法表集合

<!-- 方法表集合 -->
00 02 		// methods_count 表示表示方法表集合中包含 2 個方法

3.7.1.?第 1 個方法表

<!-- 第一個方法表 -->
00 01 		// access_flags 表示方法修飾符為public 占用 2 個字節
00 07		// name_index 方法名稱存放在常量池第 7 個 占用 2 個字節
00 08		// descriptor_index 表示方法描述存放在常量池第 8 個 占用 2 個字節
00 01		// attributes_count 表示此方法屬性表集合中有 1 項屬性 占用 2 個字節
<!-- attribute_info (code)-->
00 09		// attribute_name_index 表示屬性存放在常量池第 9 項 占用 2 個字節
00 00 00 1d // attribute_lenth 表示該屬性的長度 占用 4 個字節
00 01  		// max_stack 操作數棧的最大深度 占用 2 個字節
00 01 		// max_locals 局部變量表所需的存儲空間 占用兩個字節
00 00 00 05 // code_length 表示字節碼長度,占用 4 個字節
2a b7 00 01 b1 // code 用于存儲編譯后的字節碼指令 占用code_length 個字節00 00 		// exception_table_length 異常表長度 占用 2 個字節00 01 		// code屬性中的 attributes_count 占用兩個字節
00 0a 		// attribute_name_index 屬性名稱在常量池的第10項常量
00 00 00 06 // LineNumber類型的 attribute_length 屬性長度 占用 4 個字節
00 01 		// line_number_table_length 表示有幾個line_number_info類型的數據 占用 2 個字節
<!-- line_number_info -->
00 00 		// start_pc 字節碼行號
00 01 		// line_number java源碼行號

3.7.2. 第 2 個方法表

<!-- 第二個方法表 -->
00 01 		// access_flags 表示方法修飾符為public 占用 2 個字節
00 0b 		// name_index 方法名稱存放在常量池第 11 個 占用 2 個字節
00 0c 		// descriptor_index 表示方法描述存放在常量池第 12 個 占用 2 個字節
00 01 		// attributes_count 表示此方法屬性表集合中有 1 項屬性 占用 2 個字節
<!-- attribute_info (code) -->
00 09 		// attribute_name_index 表示屬性存放在常量池第 9 項 占用 2 個字節
00 00 00 1f // attribute_lenth 表示該屬性的長度 占用 4 個字節
00 02		// max_stack 操作數棧的最大深度 占用 2 個字節
00 01 		// max_locals 局部變量表所需的存儲空間 占用 2 個字節
00 00 00 07 // code_length 表示字節碼的長度 占用 4 個字節
2a b4 00 02 04 60 ac// code 用于存儲編譯后的字節碼指令 占用code_length個字節
00 00 		// exception_table_length 異常表長度 占用 2 個字節
00 01 		// cod屬性中的 attributes_count 表示有幾個屬性 占用兩個字節
00 0a 		// attribute_name_index 屬性名在常量池中的索引 表示是常量池中第 10 個索引 占用 2 個字節
00 00 00 06 // LineNumber類型的 attribute_length 屬性長度占用 4 個字節
00 01 		// line_number_table_length 表示有幾個 line_number_info類型的數據 站喲 兩個字節
<!-- line_number_info -->
00 00 		// start_pc 字節碼行號
00 04 		// line_number java源碼行號

3.8.?Code 屬性表與 LineNumberTable 屬性表

<!-- 第一個方法 attribute_info (code)-->
00 09		// attribute_name_index 表示屬性存放在常量池第 9 項 占用 2 個字節
00 00 00 1d // attribute_lenth 表示該屬性的長度 占用 4 個字節
00 01  		// max_stack 操作數棧的最大深度 占用 2 個字節
00 01 		// max_locals 局部變量表所需的存儲空間 占用兩個字節
00 00 00 05 // code_length 表示字節碼長度,占用 4 個字節
2a b7 00 01 b1 // code 用于存儲編譯后的字節碼指令 占用code_length 個字節00 00 		// exception_table_length 異常表長度 占用 2 個字節00 01 		// code屬性中的 attributes_count 占用兩個字節
00 0a 		// attribute_name_index 屬性名稱在常量池的第10項常量
00 00 00 06 // LineNumber類型的 attribute_length 屬性長度 占用 4 個字節
00 01 		// line_number_table_length 表示有幾個line_number_info類型的數據 占用 2 個字節
<!-- line_number_info -->
00 00 		// start_pc 字節碼行號
00 01 		// line_number java源碼行號<!--第二個方法 attribute_info (code) -->
00 09 		// attribute_name_index 表示屬性存放在常量池第 9 項 占用 2 個字節
00 00 00 1f // attribute_lenth 表示該屬性的長度 占用 4 個字節
00 02		// max_stack 操作數棧的最大深度 占用 2 個字節
00 01 		// max_locals 局部變量表所需的存儲空間 占用 2 個字節
00 00 00 07 // code_length 表示字節碼的長度 占用 4 個字節
2a b4 00 02 04 60 ac// code 用于存儲編譯后的字節碼指令 占用code_length個字節
00 00 		// exception_table_length 異常表長度 占用 2 個字節
00 01 		// cod屬性中的 attributes_count 表示有幾個屬性 占用兩個字節
00 0a 		// attribute_name_index 屬性名在常量池中的索引 表示是常量池中第 10 個索引 占用 2 個字節
00 00 00 06 // LineNumber類型的 attribute_length 屬性長度占用 4 個字節
00 01 		// line_number_table_length 表示有幾個 line_number_info類型的數據 站喲 兩個字節
<!-- line_number_info -->
00 00 		// start_pc 字節碼行號
00 04 		// line_number java源碼行號

3.9. SourceFile 屬性

<!-- 屬性表 -->
00 01 		// attributes_count 表示有幾個屬性 占用兩個字符
<!-- attribute_info -->
00 0d 		// attribute_name_index 屬性名稱在常量池中的索引 占用 2 個字節
00 00 00 02 // attribute_length 屬性長度 占用 4 個字節
00 0e		// sourcefile_index 資源文件名稱在常量池中的索引

到此為止,class文件全部解析完成,讀者也可以使用該Java源代碼自己進行編譯按照該思路進行分析,相信你們一定也會有所收獲!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/164370.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/164370.shtml
英文地址,請注明出處:http://en.pswp.cn/news/164370.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

R語言數據縮放-1到1

目錄 普通scale -1到1限定范圍scale 普通scale R語言實戰&#xff1a;scale&#xff08;&#xff09;函數 - 知乎 (zhihu.com) scale(x, center TRUE, scale TRUE) 過程&#xff1a; 對每個變量&#xff08;列&#xff09;計算平均值&#xff08;mean&#xff09;和標準…

QT中樣式表常見屬性與顏色的設置與應用

常見樣式表屬性 在Qt中的樣式表(QSS)中,有一些特定的英文單詞和關鍵字用于指定不同的樣式屬性。以下是常見的一些英文單詞和關鍵字: 顏色(Colors): color: 文本顏色 background-color: 背景顏色 border-color: 邊框顏色 字體(Fonts): font: 字體 font-family: 字體…

任意文件下載漏洞(CVE-2021-44983)

簡介 CVE-2021-44983是Taocms內容管理系統中的一個安全漏洞&#xff0c;可以追溯到版本3.0.1。該漏洞主要源于在登錄后臺后&#xff0c;文件管理欄存在任意文件下載漏洞。簡言之&#xff0c;這個漏洞可能讓攻擊者通過特定的請求下載系統中的任意文件&#xff0c;包括但不限于敏…

python3實現grep命令

由于windows上面沒有類似linux上面的grep命令&#xff0c;所以下面的python腳本來代替其能力。 grep.py import argparse import reif __name__ __main__:arg_parser argparse.ArgumentParser()arg_parser.add_argument("grep")arg_parser.add_argument("fil…

圖像導向濾波

導向濾波&#xff08;Guided Filter&#xff09;是一種基于局部線性模型的濾波方法&#xff0c;用于圖像處理中的去噪、圖像增強和邊緣保留等任務。它結合了引導圖像&#xff08;guide image&#xff09;和輸入圖像來實現對輸入圖像的濾波操作。 原理 數學原理&#xff1a; …

文件名稱管理文件:抓關鍵字歸類文件,讓文件管理變得簡單明了

在當今數字時代&#xff0c;每天都要處理大量的文件&#xff0c;無論是文本、圖片、視頻還是其他類型的文件。如何有效地管理這些文件&#xff0c;能夠迅速找到所需的信息&#xff0c;已經成為了一個重要的問題。文件名稱是文件內容的第一反映&#xff0c;也是識別和檢索文件的…

408-數據結構-代碼題

2014 2014 二叉樹&#xff08;鏈式存儲&#xff09; #include<iostream> #include<bits/stdc.h> using namespace std;typedef struct Node{struct Node *left;struct Node *right;int high0;double weight; }node;double sum0;void visit(node *t){int lop0;if…

算法刷題-動態規劃2(繼續)

算法刷題-動態規劃2 珠寶的最高價值下降路徑最小和使用最小花費爬樓梯整數拆分 珠寶的最高價值 題目 大佬思路 多開一行使得代碼更加的簡潔 移動到右側和下側 dp[ i ][ j ]有兩種情況&#xff1a; 第一種是從上面來的禮物最大價值&#xff1a;dp[ i ][ j ] dp[ i - 1 ][ j ]…

【CCF-PTA】第03屆Scratch第02題 -- 計算天數

計算天數 【題目描述】 一年有 365 天還是有 366 天呢&#xff1f;要看這一年是不是閏年。有個計算方法可以幫助我們判斷&#xff0c;那就是閏年能夠除盡 4 但不能除盡 100 或者能夠除盡 400 的年份。如果這一年是閏年&#xff0c;2 月份的天數就是 29 天。小明決定編寫一個程…

排序算法--希爾排序

實現邏輯 ① 先取一個小于n的整數d1作為第一個增量&#xff0c;把文件的全部記錄分成d1個組。 ② 所有距離為d1的倍數的記錄放在同一個組中&#xff0c;在各組內進行直接插入排序。 ③ 取第二個增量d2小于d1重復上述的分組和排序&#xff0c;直至所取的增量dt1(dt小于dt-l小于……

JSP:Servlet

Servlet處理請求過程 B/S請求響應模型 Servlet介紹 JSP是Servlet的一個成功應用&#xff0c;其子集。 JSP頁面負責前臺用戶界面&#xff0c;JavaBean負責后臺數據處理&#xff0c;一般的Web應用采用JSPJavaBean就可以設計得很好了。 JSPServletJavaBean是MVC Servlet的核心…

【實驗筆記】C語言實驗——降價提醒機器人

降價提醒機器人 題目&#xff1a; 小 T 想買一個玩具很久了&#xff0c;但價格有些高&#xff0c;他打算等便宜些再買。但天天盯著購物網站很麻煩&#xff0c;請你幫小 T 寫一個降價提醒機器人&#xff0c;當玩具的當前價格比他設定的價格便宜時發出提醒。 輸入格式&#xf…

人工智能教程(一):基礎知識

目錄 前言 什么是人工智能&#xff1f; 教學環境搭建 向量和矩陣 前言 如果你是關注計算機領域最新趨勢的學生或從業者&#xff0c;你應該聽說過人工智能、數據科學、機器學習、深度學習等術語。作為人工智能系列文章的第一篇&#xff0c;本文將解釋這些術語&#xff0c;并搭…

k8s部署-kuboard安裝(工具kuboard-spary)

Kuboard-Spray Kuboard-Spray 是一款可以在圖形界面引導下完成 Kubernetes 高可用集群離線安裝的工具 配置要求 對于 Kubernetes 初學者&#xff0c;在搭建K8S集群時&#xff0c;推薦在阿里云或騰訊云采購如下配置&#xff1a;&#xff08;您也可以使用自己的虛擬機、私有云等…

HCIP --- HCIA(部分匯總)--- 點對點網絡

抽象語言 --- 電信號 抽象語言 --- 編碼 編碼 --- 二進制 二進制 --- 電信號 處理電信號 OSI/RM ---- 開放式系統互聯參考模型 --- 1979 --- ISO --- 國際標準化組織 核心思想 --- 分層 應用層 --- 提供各種應用程序&#xff0c;抽象語言轉換成編碼&#xff0c;人機交互…

Docker 命令詳解

1. 容器生命周期管理 命令說明文檔run創建一個新的容器并運行一個命令Docker run 命令start/stop/restart啟動、停止、重啟容器Docker start/stop/restart 命令kill殺掉一個運行中的容器Docker kill 命令rm刪除一個或多個容器Docker rm 命令pause/unpause暫停 恢復容器中所有的…

Arm64版本的centos編譯muduo庫遇到的問題的歸納

環境&#xff1a;Mac m2 pro下的VMware虛擬機中Arm64 centos ./build.sh 執行后提示如下 cmake -DCMAKE_BUILD_TYPErelease -DCMAKE_INSTALL_PREFIX…/release-install-cpp11 -DCMAKE_EXPORT_COMPILE_COMMANDSON /root/package/muduo-master – Boost version: 1.69.0 – Co…

[git] 忽略已經提交的文件或文件夾

文件已經被Git跟蹤 如果某個文件已經被Git跟蹤過&#xff08;即已經添加到版本控制中&#xff09;&#xff0c;.gitignore文件對該文件將不起作用。您需要使用以下命令將該文件從Git中移除&#xff1a; git rm --cached 支持文件夾 -r <文件夾>

Flink Table API 讀寫MySQL

Flink Table API 讀寫 MySQL import org.apache.flink.connector.jdbc.table.JdbcConnectorOptions; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.table.api.DataTypes; import org.apache.flink.table.api.Envi…