1. 程式人生 > >小師妹學JVM之:java的位元組碼byte code簡介

小師妹學JVM之:java的位元組碼byte code簡介

[toc] # 簡介 Byte Code也叫做位元組碼,是連線java原始碼和JVM的橋樑,原始碼編譯成為位元組碼,而位元組碼又被載入進JVM中執行。位元組碼怎麼生成,怎麼檢視位元組碼,隱藏在Byte Code背後的祕密是什麼呢?快跟小師妹一起來看看吧。 # Byte Code的作用 小師妹:F師兄,為什麼Java需要位元組碼呢?直接編譯成為機器碼不是更快嗎? 小師妹,Java的設計初衷是一次編寫,到處執行。為了相容各個平臺的執行環境,java特別為各種平臺設計了JVM。 我們可以把JVM看做是一種抽象,對外提供了統一的介面。這樣我們只需要編寫符合JVM規範的程式碼,即可在JVM中執行。 回想下之前我們提到過的java的執行過程: ![](https://img-blog.csdnimg.cn/20200524212920415.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70) 1. 編寫java程式碼檔案比如Example.java 2. 使用java編譯器javac將原始檔編譯成為Example.class檔案 3. JVM載入生成的位元組碼檔案,將其轉換成為機器可以識別的native machine code執行 小師妹:F師兄,我有一個大膽的想法,JVM的作用是將位元組碼解釋或者編譯成為機器碼。然後在相應的執行環境中執行。那麼有沒有可能,不需要JVM,不需要機器碼,而是直接在對應的平臺上執行位元組碼呢? 愛因斯坦說過沒有想像力的靈魂,就像沒有望遠鏡的天文臺。小師妹你這個想法很好,這種實現有個專業的說法叫做:Java processor。 Java processor就是用硬體來實現的JVM。因此位元組碼可以直接在Java processor中執行。 其中比較出名的是Jazelle DBX,這是一個主要支援J2ME環境的硬體架構。為了提升java在手機端的執行速度。 但是這樣做其實也是有缺點的,後面我們會講到,java位元組碼中的指令非常非常多。所以如果用硬體來實現的話,就會非常非常複雜。 一般來說Java processor不會實現全部的位元組碼中的功能,只會提供部分的實現。 # 檢視Byte Code位元組碼 小師妹:F師兄,那使用javac編譯過後的class檔案跟位元組碼有什麼關係呢? class檔案中大部分都是byte code,其他的部分是一些meta data元資料資訊。這些組合在一起就是class檔案了。 小師妹:F師兄,你說class檔案是byte code,為什麼我在IDE中開啟的時候,直接顯示的是反編譯出來的原始檔呢? 小師妹,這是IDE的一個便利功能。因為大多數情況下,沒有人想去看class檔案的Byte code的,大家都是想去看看這個class檔案的原始檔是什麼樣的。 我們舉個最簡單的例子: ![](https://img-blog.csdnimg.cn/2020053021330297.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70) 這個類中,我們定義了一個很簡單的testByteCode方法,裡面定義了兩個變數,然後返回他們兩個的和。 現在有兩種方法來檢視這個類的Byte Code: 第一種方法是用javap命令: ~~~java javap -c ByteCodeUsage.class ~~~ ![](https://img-blog.csdnimg.cn/20200530213721962.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70) 生成的結果如上所示。 第二種方法就是在IDEA中,選中class檔案,然後在view中選中show Bytecode: ![](https://img-blog.csdnimg.cn/20200530214606575.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70) 我們看下輸出結果: ![](https://img-blog.csdnimg.cn/20200530215213620.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70) 兩個的結果在顯示上面可能有細微的差異,但是並不影響我們後面對其的解析。 # java Byte Code是怎麼工作的 小師妹:F師兄,能講解一下這些byte code到底是怎麼工作的嗎? 首先我們要介紹一下JVM的實現是基於棧的結構的。為什麼要基於棧的結構呢?那是因為棧是最適合用來實現function互相呼叫的。 我們再回顧一下上面的testByteCode的位元組碼。裡面有很多iconst,istore的東西,這些東西被稱作Opcode,也就是一些基於棧的操作指令。 上面講了java bytecode的操作指令其實有很多個。下面我們列出這些指令的部分介紹: ![](https://img-blog.csdnimg.cn/20200530223345376.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70) 實在是太多了,這裡就不把所有的列出來了。 我們看到的指令名字其實是一個助記詞,真實的Opcode是一個佔用兩個位元組的數字。 下面我們來詳細解釋一下testByteCode方法: ~~~java public int testByteCode(); Code: 0: iconst_1 1: istore_1 2: iconst_2 3: istore_2 4: iload_1 5: iload_2 6: iadd 7: ireturn ~~~ 第一步,iconst_1將int 1載入到stack中。 第二步,istore_1將入棧的int 1出棧,並存儲到變數1中。 第三步,iconst_2將int 2入棧。 第四步,istore_2將入棧的int 2出棧,並存儲到變數2中。 第五步,iload_1將變數1中的值入棧。 第六步,iload_2將變數2中的值入棧。 第七步,iadd將棧中的兩個變量出棧,並相加。然後將結果入棧。 第八步,ireturn將棧中的結果出棧。 這幾步實際上完美的還原了我們在testByteCode方法中定義的功能。 當然我們只介紹了最賤的byte code命令,通過這些簡單的命令可以組合成為更加複雜的java命令。 # 總結 本文介紹了java byte code的作用和具體的指令,並分析了一個簡單的例子來做說明。希望大家能夠掌握。 本文的例子[https://github.com/ddean2009/learn-java-base-9-to-20](https://github.com/ddean2009/learn-java-base-9-to-20) > 本文作者:flydean程式那些事 > > 本文連結:[http://www.flydean.com/jvm-byte-code/](http://www.flydean.com/jvm-byte-code/) > > 本文來源:flydean的部落格 > > 歡迎關注我的公眾號:程式那些事,更多精彩等著您!