java jpg结构_Java Class 字节码文件结构分析----附带逐字节码分析图
歡迎訪問我的博客,同步更新: 楓山別院 今天給大家分享下Java Class字節碼文件的結構解析。(文末附有詳細大圖)
代碼
首先我們創建一個Java類,然后添加一些成員變量和方法,如下:
public class Test {
String name = "hello";
int size = 10;
long num = 100;
Double pi = 3.14;
public static void main(String[] args) {
Test test = new Test();
test.print();
}
public void print() {
System.out.print(name);
}
}
大家可以看到,這是一個非常簡單的類,僅僅有幾個基本類型的成員變量和兩個簡單的方法。這是為了我們分析Class文件方便起見,沒有添加復雜的結構。
編譯
然后大家可以用javac命令編譯一下這個類。編譯之后,我們就得到了一個class文件,用sublime之類的軟件打開這個class文件,我們可以看到如下信息:
CA FE BA BE 00 00 00 34 00 40 0A 00 11 00 29 08
00 2A 09 00 0C 00 2B 09 00 0C 00 2C 05 00 00 00
00 00 00 00 64 09 00 0C 00 2D 06 40 09 1E B8 51
..........
00 00 00 0C 00 01 00 00 00 0B 00 1F 00 20 00 00
00 01 00 27 00 00 00 02 00 28
是一行一行的十六進制的字符,每兩個字符是一個字節,每個字節之間用空格分隔,我省略掉了中間的多行數據,如果要看完整數據,請看文末。
這些就是編譯之后的信息了,文件中的字符在我們看來只是一行一行字符,其實是劃分格式的。我們來看一下是如何劃分的。
結構
Class文件的結構,是一個結構體,有以下元素,描述如下:
ClassFile {
u4 magic; //魔數
u2 minor_version; //Java的次版本號
u2 major_version; //Java的主版本號
u2 constant_pool_count; //常量池的長度
cp_info constant_pool[constant_pool_count]; //常量池數組
u2 access_flags; //類訪問標志
u2 this_class; //類名索引
u2 super_class; //父類名索引
u2 interface_count; //實現的接口的數量
u2 interfaces[interface_count]; //接口數組
u2 fields_count; //字段數量
field_info fields[fields_count]; //字段數組
u2 methods_count; //方法數量
method_info methods[methods_count]; //方法數組
u2 attributes_count; //屬性數量
attribute_info attributes[attributes_count]; //屬性數組
}
左邊u2,u4都是表示字節的長度,u2是兩字節,u4是四字節。右邊是元素的名稱。
Class文件就是由以上元素,一個緊挨一個組成的。總體的結構還是不復雜的,開頭的三個值,magic,minor_version,major_version和中間的access_flags,this_class,super_class,他們是固定的位置,固定的字節長度。其他的數量不一定的值,都是一個長度,然后后面就是緊跟著該長度的數組,存儲著該組值。數組結構通常都有下一級的子結構。
我把字節文件按照上面的元素格式,以不同的顏色劃分了出來,請大家看下結構圖:
class-simple.png
看圖非常的一目了然,讀兩遍書,不如看一遍圖啊。
連續的相同顏色的格子,是同一種元素的值。元素之間是緊緊的排列在一起,大家可以看到結構非常的緊湊,節省空間。看不清的同學可以下載下來,放大一下看。
分析
1. magic 魔數
我們知道有時候通過后綴識別文件是不準確的,因為很容易就改掉后綴。所以很多文件格式,在文件的開頭寫上幾個固定的值,為了識別方便該種格式。比如PDF文件的開頭是“%PDF”,這個固定的值,就叫魔數,其實就是個標識。class文件的魔數就是“CA FE BA BE”,JVM讀取開頭的四個字節,如果是這個值,那么就認為這個文件是個class。
magic.jpg
2. version 版本號
魔數之后,是版本號,大家可以看到,我們有兩部分的版本號。第一個版本號就是次版本號,第二個是主版本號。比如52.0,52是主版本號,0是次版本號。這個版本是為了讓JVM識別編譯這個clas文件的Java版本,比如Java SE8,對應的版本號是52.0,Java SE7 是51.0,Java SE6 是50.0。如果一個最高只支持Java SE7版本的JVM,讀取到一個52.0,那么它可能是執行不了這個class的,以為它是Java SE8編譯出來的,可能用了JavaSE8的新特性。
version.jpg
3. constant_pool 常量池
常量池占用了class中非常多的空間,存放著非常多的信息,包括數字,字符串,類和接口的名字,字段和方法的名字等等。上來先是常量池的數量,也就是常量池數組的長度。后面緊跟著就是數組的內容,非常長,結構也不太一樣。大家可以仔細看看附錄中的詳細圖,太大了,在這就不截圖了。
常量池中元素的子結構雖然有非常多的種類,但是都差不多的。首先是tag,tag的值表示這個元素是一個什么類型,也就是一個什么數據結構,然后JVM就可以根據這個結構來解析數據了。一般的XXX_index就是一個索引,length就是一個長度,bytes中是該元素存儲的值。結構非常多,我們后面單獨一篇文章介紹下這里。
4. access_flags 類訪問標志
access_flags保存的信息是,該類的訪問標志,比如是public還是private,是個接口還是個類,或者枚舉,等等。
access_flags.jpg
此處 access_flags的值是0x0021,代表什么意思呢?我們需要看個表格:
名稱
值
說明
ACC_PUBLIC
0x0001
public類型
ACC_FINAL
0x0010
final修飾符
ACC_SUPER
0x0020
使用invokespecial字節碼指令,在JDK1.2之后添加
ACC_INTERFACE
0x0200
接口
還有好幾種類型,我們就先看這些。我們的值是0x0021,好像沒有這個值對應的類型。其實是,0x0020 加上 0x0001,也就是說0x0021表示ACC_PUBLIC和ACC_SUPER。也就是說,這是個public訪問級別的類。
5. this_class 類名索引
this_class元素保存的是類的全限定類名,即包路徑加類名。為什么還有個索引呢?因為這個地方保存的不是具體的全限定類名的字符串,是一個索引值。這個索引是常量池的索引,也就是說,其實真正的全限定類名字符串是在常量池存著呢。我們在附錄的詳細表中找一下這個值,找到第39行數據的最后,是this_class的位置。
this_class
它的值是0x000c,是十進制的12對吧?我們找常量池中的索引是12的值,在第4行最后。
index_12.jpg
很奇怪,這個地方比不是我們說的全限定類名字符串,是一個叫 name_index的索引。其實這個位置存放的數據,是有一個子結構的,它由tag和 name_index組成。tag的值是7,這個值7代表了一種數據結構,就是 CONSTANT_Class_info,這個類型保存的值是類或者接口的符號引用。又是一個引用,也就是索引。從表上我們可以知道,這個索引的值是49,OK,我們再從常量池找索引49的值。
index_49.jpg
在第26和27行上,我們終于找到了類的全限定名稱。
6. super_class 父類名索引
super_class跟this_class是一樣的,不過這里保存的是類的父類全限定名稱而已。大家可以自己找一下看看。不過需要注意的是,如果沒有明確指定某個類的父類,在Java中默認父類都是java.lang.Object,而java.lang.Object本身是沒有父類的,所以如果是Object類,它的super_class值就是0。
7. interfaces 接口池
接著就是接口索引表或者說是接口池了,這里保存類實現的接口。
interfaces.jpg
因為我們的類沒有實現接口,所以它的長度是0,后面的數組自然也就省略了。
8. fields 字段池
fields.jpg
接著是我們的字段信息。從 fields_count的值我們可以知道有4個字段,這跟我們在代碼中的字段數量是一致的。然后緊接著就是字段數組了。字段數組中的元素是有數據結構的。如下:
field_info {
u2 access_flags; //變量的訪問標識符
u2 name_index; //名稱索引
u2 descriptor_index; //類型信息索引
u2 attributes_count; //自定義屬性長度
attribute_info attributes[attributes_count]; //自定義屬性池
}
name_index中存的就是字段名字的索引,老規矩大家自己找一下。access_flags是變量的訪問標識符,descriptor_index保存的是變量的類型信息。最后剩下的屬性,為什么有了類的屬性池還會在這里有個字段下的屬性池呢?這個字段下的屬性池是留給JVM來自定義拓展的,各個JVM實現可能會不一樣,JVM遇到識別不了的屬性會自動跳過。
9. methods 方法池
大家可以看到,在附錄詳細大圖中,方法池沒有展開詳細的結構,這是因為這里太復雜了,展開圖不太好畫,我太懶了,所以沒畫,哈哈哈哈。依然是上來就是一個長度,3個方法,后面跟著一個數組。有人要問了,不對啊,怎么是3個方法,我們只寫了2個。那是因為還有個類的默認構造方法,忘了吧,哈哈。后面有機會我們詳細分析方法池的結構。
10. attributes 屬性池
屬性池這個地方,跟常量池一樣,保存了很多信息,不過我們這個類中的屬性信息較少。屬性也有很多的子結構,而且不同的JVM實現還可能會有自己的屬性值,這里我們后面有機會單獨說一下,大家現在知道即可。
附錄
最后,附上一張非常詳細的大圖,我在圖中畫出了更詳細的結構,包括常量池中子元素的結構,每個字節對應的值是什么等等,非常詳細,也耗費了我很多時間,希望對大家有用。大家可以下載下來,經常看一看。
class.png
總結
以上是生活随笔為你收集整理的java jpg结构_Java Class 字节码文件结构分析----附带逐字节码分析图的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 847方波放大电路_分析运放7大经典电路
- 下一篇: 西门子安装未找到ssf文件_V5.3安装