文章目錄
class 文件結構深入解析
什麼是 class 文件
能夠被 JVM 識別,加載並執行的文件格式,他就類似於 mp3 的格式,只能運用於特定的地方,如 mp3 只能被播放,而 class 文件則是需要被 JVM 進行加載。
並不是只有 java 語言才能生成 class 文件,當然還有其他的一下語言:
如何生成一個 class文件
通過 ide 自動生成 class 文件,通過 run 來執行 calss 文件
通過 javac 生成 class 文件 ,通過 java 命令去執行 class 文件
class 文件的作用
記錄一個類文件中的所有信息,記住是所有的信息,包括類名稱,方法,變量等,class 中記錄的信息遠遠多於java源代碼中的信息。例如:我們並沒有在類中定義 this、super 這樣的關鍵字,但是我們可以使用,這是因爲 java 虛擬機在生成 class 文件時已經幫我們記錄在裏面了。
class 文件結構
一種8位字節的二進制文件
各個數據按順序緊密的排列,沒有間隙,不想有些文件,爲了讀取方便,則不會讓數據緊密排列。而 class 文件這樣的好處就是 體積小。
每個類或者接口都單獨佔據一個 class 文件
class 的文件格式採用的是類似結構體的結構來存儲數據,這種結構只有兩種數據類型:無符號豎和表,其中無符號數屬於基本的數據類型,u1,u2,u4,u8 來分別代表 一個字節 ,2,4,8個字節。無符號數可以用阿里描述數字,索引引用,數量值或者 utf-8構成的字符串值,而表是由多個無符號數或其他表構成的複合數據結構,所有的表都以 _info 結尾,表用於描述有層次關係的複合結構數據類型,其實整個 class 文件就是一張表。(但是我覺得更像結構體)
下面看一下具體的結構
類型 | 名稱 | 數量 | 作用 |
---|---|---|---|
u4(無符號四字節) | magic | 1 | 加密段,當前calss 文件是否被串改過 |
u2 | minor_version | 1 | 最小可以被那個版本的 jdk所加載 |
u2 | major_version | 1 | 當前calss是別那個版本生成的 |
u2 | constant_pool_count | 1 | 常量池的數量,通常只有一個 |
cp_info | constant_pool | constant_poll_count - 1 | 真正的常量池,它內部包含了很多內容,下面有詳細解釋 |
u2 | access_flags | 1 | 作用域標誌,例如這個 class 文件是 public 還是 public final 類型的 等等。下面會放一張圖 |
u2 | this_class | 1 | this,jvm 在生成 class 的時候幫我們補充了這個字段,這就是爲什麼我們沒有定義它卻能夠使用他了 |
u2 | super_class | 1 | 和上面的 this 一樣 |
u2 | interfaces_count | 1 | 數量 |
u2 | interfaces | interfaces_count | 這兩個表示當前類實現了多少個接口,注意:只算直接的,間接的不算,如不會計算父類實現的接口。 |
u2 | fields_count | 1 | 數量 |
field_info | fields | fields_count | 當前class中的所有成員變量,他裏面還包含了其他的信息,如 類型,所屬的類等 |
u2 | medthods_count | 1 | 數量 |
medthod_info | methods | methods_sount | 記錄了class 中所有的方法,包含了其他信息。 |
u2 | attribute_count | 1 | 數量 |
attribute_info | attributes | attributes_count | 記錄類屬性相關的,上面沒有包含的都會在這裏面,如註解等。 |
我們看一下這個表格,class 文件中定義了很多字段,這些字段又包含了非常多的內容。通過這些字段,jvm 就可以找到我們類中所有的內容
access_flags :作用域
這個圖清楚地表示了 access_flags 字段的作用域。
constant_pool:常量池,下面是幾種常用的類型
CONSTANT_Integer_info
CONSTANT_Long_info
CONSTANT_String_info
// 下面這幾個裏面存儲的並不是真的內容,而是索引,這些索引最終指向的就是上面這幾種類型,所以我們所有的信息都是存在常量池中的。
CONSTANT_Class_info //類信息,例如名字等,引用到類的一些信息等
CONSTANT_Fieldref_info //類中變量信息
CONSTANT_Methodref_info //類中方法信息
我們可以通過工具來查看一下 class 文件內容,工具名字爲 010 Editor。
struct cp_info_constant_pool[0] 中的 u2,代表的是無符號數,u2 代表訪問標誌,如 u2 class_index 指向的就是這個方法所屬的類。相當於索引吧,
注意看 u1 tag:
在常量池中有14種類型,這個常量都是一個表,每一個表都有各自組成的機構,這寫常量都有一個特點,每個常量的開始都是一個用 u1 類型的無符號數表示的標誌位,如下表所示:
常量池中第一個 struct cp_info constant_poll[0] ,他的後面是 Methodref ,標誌是 10 ,我們和上面的表對比一下,找到第 10 個,也是 Methodref ,通過這個表,我們可以查看 u1 tag 到底表示的是那種類型。
如上面的 fields_count 爲1,下面就有一個 struct field_info 的表,裏面保存了字段的信息,在紅框的下面,methods_count 爲 2,下面就有 methods[0] 和 methods[1]表,來表示這兩個方法。這也就對應了上面的表。
class 文件弊端
內存佔用大,不適合移動端
堆棧的加載模式,加載速度慢
文件 IO 操作多,類查找慢 。每次加載類的時候都要去尋找和加載
dex文件結構深入解析
什麼是 dex 文件
能夠被 DVM 所識別,加載並執行的文件格式,dex 文件可以 用 c 和c++ 進行生成
如何生成一個 dex 文件
通過 IDE 自動幫我們 build 生成
手動通過 dx 命令生成 dex 文件
dex 文件的作用
記錄整個工程中所有類文件的信息,是整個工程(class 則是記錄當前類的信息)
dex 文件結構
一種 8 位字節的二進制流文件
各個數據按順序緊密的排列,無間隙
整個應用中所有的 java 源文件都放在一個dex中,這裏不考慮 multidex
dex 文件頭
-
magic :一般稱爲魔數,他可以判斷當前的 dex 文件是否有效,可以看到他的 大小就是 8 h,也就是他用了 8個1字節的無符號數表示。
-
checksum : dex 文件的校驗和,通過它可以判斷 dex 文件是否被損壞
-
signature[] : 用於校驗 dex 文件,其實就是把整個 dex 文件用 SHA-1 簽名得到的一個值,
-
file_size : 表示真個文件的大小,佔用了4個字節
-
header_size : 表示 dex 頭部分的大小,佔用4字節
-
endian_tga :字節序標記,用於指定 dex 運行環境的 cpu,預設值爲0x123456789
-
link_size 和 link_off :分別指定了鏈接段的大小和文件的偏移,通常都爲0
-
map_off :指定了 DexMapList 的文件偏移
-
string_ids_size 和 string_ids_offf:這兩個字段表示了 dex 文件中所有用到的字符串的個數和位置偏移。通過轉換 size 爲16,偏移爲112。
下面我們可以看一下 string_ids_size 的16進制和off 的16進制,分別爲10 00 00 00 和 70 00 00 00,前者轉換10進制後爲 16,說明 size 有 16 個字符串,off 則代表偏移 70h , 最後一個空字符“0”表示的是結尾 。然後我們找到70h 看一下
下面我們看一下70h
從70 開始,到 AOh 結束,所選中的都是 字符串的偏移量,通過這些偏移量我們才能找字符串,看一下上面第二個框,是 字符串的索引,可以看到他也是從70h 開始,大小是40h,也就是到A0h。在這段區域內保存的纔是真正的字符串索引。我們可以看一下70h 第一個 92 01 00 00 ,他代表的偏移地址就是 0192h,接着我們找一下 0192h
可以看到我一共選中了 8個字節,在這8個字節中我們可以用到的有6個,最開始的 06 則表示我們用到的個數,最後的 0 表示的是字符串結尾,下面我們把他們進行轉換一下:
十六進制 | 3C | 69 | 6E | 69 | 74 | 3E |
---|---|---|---|---|---|---|
十進制 | 60 | 105 | 110 | 105 | 116 | 62 |
ASCII | < | i | n | i | t | > |
通過上面這種方式我們就可以找到字符串的16進制,並轉換爲對應的字符串。
其他的都一樣,我們也沒有必要像上面遮這樣找,其實我們可以直接在索引區找到對應的值。例如上面這個例子:
這裏的數據是從 163開始的,是因爲他並沒有及時前面的 06 ,這個06至少代表後面要用到 6 個而已。
從 9 往下基本都是這個樣子的。通過上面這種方法就可以查到具體的位置。
索引區
類型索引:
如 Hellow 類索引,Object 索引,String 索引的。這些都是我們所引用到的。
方法原型索引 :
字段索引:
方法索引:
如上面的 main 方法 ,printStream 打印方法等。。他會記錄當前類所引用的方法索引和繼承的方法索引
類索引:
整個dex 文件中所有類索引
map 列表:
他是對整個頭文件的一個校驗
數據區
每個索引對應的值就是數據了。
最後看一下整個 dex 文件
Class 和 Dex 的區別
ass 文件是一個表。這個表只記錄了當前java的信息。
dex 將文件劃分爲了 三個區域,這三個區域存儲了整個工程中所有的java 文件的信息,所以 dex 在類越來越多的時候優勢就提現出來了。他只要一個dex文件,很多區域都是可以進行復用的,減少了dex 文件的大小。
本質上他們是一樣的,dex 是從 class 文件演變而來的,但是 calss 中存在了許多沉餘信息,dex 去掉了沉餘信息,並進行了整合
參考:https://www.jianshu.com/p/e5062d62a3d1
慕課網視頻