Java字节码解读
Java最重要的一個機制就是虛擬機JVM,通過執(zhí)行編譯好的字節(jié)碼.class文件,可以實現(xiàn)跨平臺的可移植性,那么小伙伴們會不會好奇字節(jié)碼文件是怎樣的,它是怎么工作的?參考來源,為了便于回顧,特此按自己思路重頭來一遍,接下來我們就一起來解讀一下字節(jié)碼文件,前方高能,大腦請?zhí)崆扒蹇铡?/p>
?
1. 前期準備
1.1 首先編寫一個測試程序,簡單地在main方法中聲明一個Integer包裝類型變量i和一個基本類型int類型變量n,代碼如下。
public class EnTest {public static void main(String[] args) {Integer i = 10;int n = i;} }1.2 命令行javac編譯得到.class字節(jié)碼文件
1.3 安裝二進制文件編輯器,博主使用Notepad++,可參照我的另一篇博客,你也可以使用其它編輯器。
?
2. 打開字節(jié)碼文件
2.1 使用安裝HexEditor插件的Notepad++打開字節(jié)碼文件,需要說明的是,文件中都是16進制,所以兩位剛好是一個字節(jié),如下圖。
?
2.2 按照對照表分析字節(jié)碼文件
2.2.1 從文件取4個字節(jié)【ca fe ba be】得到第一塊內(nèi)容,文件類型
最先的4個字節(jié)ca fe ba be代表了字節(jié)碼文件的類型,只要是class文件,開頭的四個字節(jié)就是cafebabe,音譯咖啡寶貝也就是java的logo熱騰騰的咖啡。
?
2.2.2接著從文件取2+2個字節(jié)【00 00 00 3b】?得到第二塊內(nèi)容,java版本
其中開始的【00 00】這兩個字節(jié)的十進制是0,代表,次版本號是0,;然后【00 3b】的十進制是59,代表主版本號是15,所以我的java版本是java 15
?
2.2.3接著取2+n個字節(jié)得到第三塊內(nèi)容——常量池
先取兩個字節(jié)00 19,化為十進制為25,表示有25項常量,但是第一項常量是系統(tǒng)預留,所以是24項常量,下圖是常量對應表,每一個常量由兩三項數(shù)據(jù)構成,但是每一項常量的第一個數(shù)據(jù)都是一個字節(jié)大小,所以接下來我們?nèi)∫粋€字節(jié)。
?
2.2.3.1 第一項常量,取一個字節(jié)【0a】
0a十進制是10,根據(jù)值是10確定常量類型是CONSTANT_Methodref_info,它的第二項數(shù)據(jù)占兩個字節(jié),是索引,表示指向第幾項常量。取接下來的兩個字節(jié)【00 02】十進制是2,表示指向第2項常量,現(xiàn)在還沒求第二項常量,看后面。然后第三項數(shù)據(jù)也是占兩個字節(jié),也是索引,再取接下來的【00 03】十進制是3,表示指向第3項常量,現(xiàn)在還沒求第三項常量,第一項常量結束。
?
2.2.3.2第二項常量,取一個字節(jié)【07】
07十進制是7,根據(jù)7確定常量類型是CONSTANT_class_info,它的第二項數(shù)據(jù)占兩個字節(jié),是索引,也表示指向第幾項常量,取接下來的兩個字節(jié)【00 04】十進制是4,指向第4項常量,第2項常量結束。
?
2.2.3.3第三項常量,取一個字節(jié)【0c】
0c十進制是12,根據(jù)值是12確定常量類型是CONSTANT_NameAndType_info,它的第二項數(shù)據(jù)占兩個字節(jié),是索引,表示指向第幾項常量。取接下來的兩個字節(jié)【00 05】十進制是5,表示指向第5項常量。然后第三項數(shù)據(jù)也是占兩個字節(jié),也是索引,再取接下來的【00 06】十進制是6,表示指向第6項常量,第3項常量結束。
?
2.2.3.4第四項常量,取一個字節(jié)【01】
01十進制是1,根據(jù)值是1確定常量類型是CONSTANT_Utf8_info,它的第二項數(shù)據(jù)占兩個字節(jié),是長度length。取接下來的兩個字節(jié)【00 10】十進制是16,表示長度是16。然后第三項數(shù)據(jù)占一個字節(jié),是長度為length=16的字符串,取接下來的【6a 61 76 61 2f 6c?61 6e 67 2f 4f 62 6a 65 63 74】轉化為十進制,然后根據(jù)ASCII碼轉化為一個個字符,字符串使用程序計算如下圖,得到“java/lang/Object”,第4項常量結束。
?
2.2.3.5第5項常量,取一個字節(jié)【01】?
01十進制是1,根據(jù)值是1確定常量類型是CONSTANT_Utf8_info,它的第二項數(shù)據(jù)占兩個字節(jié),是長度length。取接下來的兩個字節(jié)【00 06】十進制是6,表示長度是6。然后第三項數(shù)據(jù)占一個字節(jié),是長度為length=6的字符串,取接下來的【3c 69 6e ?69 74 3e 】轉化為十進制,然后根據(jù)ASCII碼轉化為一個個字符,字符串使用程序計算如下圖,得到“<init>”,第5項常量結束。
?
2.2.3.6第6項常量,取一個字節(jié)【01】?
01十進制是1,根據(jù)值是1確定常量類型是CONSTANT_Utf8_info,它的第二項數(shù)據(jù)占兩個字節(jié),是長度length。取接下來的兩個字節(jié)【00 03】十進制是3,表示長度是3。然后第三項數(shù)據(jù)占一個字節(jié),是長度為length=3的字符串,取接下來的【28?29 56】轉化為十進制,然后根據(jù)ASCII碼轉化為一個個字符,字符串使用程序計算如下圖,得到“()V”,第6項常量結束。
?
2.2.3.7?第7項常量,取一個字節(jié)【0a】
0a十進制是10,根據(jù)值是10確定常量類型是CONSTANT_Methodref_info,它的第二項數(shù)據(jù)占兩個字節(jié),是索引,表示指向第幾項常量。取接下來的兩個字節(jié)【00 08】十進制是8,表示指向第8項常量,現(xiàn)在還沒求第8項常量,看后面。然后第三項數(shù)據(jù)也是占兩個字節(jié),也是索引,再取接下來的【00 09】十進制是9,表示指向第9項常量,現(xiàn)在還沒求第9項常量,第7項常量結束。
?
……………………………………………………………………
……………………………………………………………………
?
以此類推可以得到其它17項常量,我就不一一推導了,使用javap -v EnTest命令得到反編譯內(nèi)容得到常量池內(nèi)容和常量池解讀結束位置如下圖。
?
2.2.4根據(jù)對照表第四塊內(nèi)容訪問標志位,取兩個字節(jié)【00 21】
【00 21】表示0x0020和0x0001進行或操作得到,訪問屬性表如下圖所示,即表示類屬性是public和繼承了父類(默認繼承Object類)。
?
2.2.5根據(jù)對照表得到第5塊內(nèi)容本類名稱,取兩個字節(jié)【00 11】
【00 11】十進制是17,表示此處從第17項常量獲得值,而且第17項常量指向第18項常量,第18項常量值為“EnTest”,代表本類名稱為“EnTest”。
?
2.2.6根據(jù)對照表得到第6塊內(nèi)容父類名稱,取兩個字節(jié)【00 11】
【00 02】十進制是2,此處從第2項常量獲得值,而且第2項常量指向第4項常量,第4項常量值為“java/lang/Object”,代表父類名稱為“java/lang/Object”。
?
2.2.7根據(jù)對照表得到第7塊內(nèi)容接口信息,先取兩個字節(jié)【00 00】
【00 00】表示接口數(shù)量為0,第7塊內(nèi)容結束。
?
2.2.8根據(jù)對照表得到第8塊內(nèi)容類級別變量信息,先取兩個字節(jié)【00 00】
該處描述類和接口的變量,不包括方法中的局部變量,我們只定義了兩個變量,所以此處為0,如果有類級別變量,可以安裝下圖字段表進行解析。
?
2.2.9根據(jù)對照表得到第9塊內(nèi)方法信息,先取兩個字節(jié)【00 02】
?
【00 02】十進制是2,表示有兩個方法,但我們只有一個main方法,我們接下來根據(jù)方法表來推斷一下另一個方法。
先取兩個字節(jié)【00 01】,該處是訪問標志符,根據(jù)訪問屬性表可知0x0001表示public,其次取兩個字節(jié)【00 05】十進制是5,表示從常量池取第5項常量值得到方法名“<init>”,然后取兩個字節(jié)【00 06】十進制是6,表示從常量池取第6項常量值得到方法描述"()V",接著取兩個字節(jié)【00 01】十進制是1,表示有一個屬性,需要根據(jù)屬性表進行解析,屬性表如下圖。
先取兩個字節(jié)表示屬性名字在常量池的索引,取兩個字節(jié)【00 13】十進制是19,第19項的值是“Code”,所以屬性名稱是“Code”,既然是Code屬性,虛擬機就會需要調(diào)用Code屬性表,不再使用上述屬性表結構解讀。
由于Code屬性表第二項的屬性長度要4個字節(jié),則取4個字節(jié)【00 00 00 1d】十進制是29,長度 是29,所以往后29個字節(jié)都是該Code屬性的內(nèi)容。接下來取兩個字節(jié)【00 01】表示max_stack=1,再取兩個字節(jié)【00 01】表示max_locals=1,再取4個字節(jié)【00 00 00 05】表示字節(jié)碼指令長度是5,接下來5個字節(jié)【2a b7 20 01 b1 ?】具體意思就不展開了,由于后續(xù)解析內(nèi)容涉及更多屬性表內(nèi)容,但是平時又不大涉及,故不多多講解,到此結束解讀。
?
?
最終使用javap -v EnTest反編譯的部分字節(jié)碼內(nèi)容如下。
?
?
總結
- 上一篇: 二进制、八进制、十六进制和十进制的相互转
- 下一篇: Java中的与、或、非以及异或( | ~