小師妹學JVM之:java的位元組碼byte code簡介
阿新 • • 發佈:2020-06-25
[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的部落格
>
> 歡迎關注我的公眾號:程式那些事,更多精彩等著您!