學習資料來自java
深刻理解JVM虛擬機segmentfault
http://www.javashuo.com/article/p-msqwsymi-a.html數組
Java虛擬機的指令由一個字節長度的,表明着某種特定操做含義的數字(簡稱操做碼,Opcode)以及跟隨其後的零至多個表明此操做所需參數(稱做操做數,Operands)來構成的。Java虛擬機採用面向操做數棧而不是寄存器(Android的Dalvik虛擬機則是依靠寄存器)架構的,因此大多數的指令都不包含操做數,只有一個操做碼。post
因爲Java虛擬機操做碼的長度限制在了一個字節(0-255),意味着指令集的操做碼總數不可能超過256條。學習
不考慮異常處理的話,Java虛擬機的解釋器可使用下面的僞代碼當作最基本的執行模型來理解:ui
do{ 自動計算PC寄存器的值+1; 根據PC寄存器的指示位置,從字節碼流中取出操做碼; if(字節碼存在操做數) { 從字節流中取出操做數; } 執行操做碼所定義的操做; }while(字節碼長度>0)
在Java虛擬機的指令集中,大多數的指令都包含了其操做所對應的數據類型信息。如iload,fload指令用於從局部變量表中加載int,float型數據到操做數棧中。lua
對於大部分與數據類型相關的字節碼指令,它們的操做碼助記符中都有特殊的字符來代表專門爲哪一種數據類型服務:i表明對int類型的數據操做,l表明long,s表明short,b表明byte,c表明char,f表明float,d表明double,a表明reference。 也有一些指令的助記符中沒有明確地指明操做類型的字母,如arraylength指令,它沒有表明數據類型的特殊字符,但操做數永遠只能是一個數組類型的對象。 還有另一些指令,如無條件跳轉指令goto則是與數據類型無關的。.net
Java虛擬機指令所支持的數據類型以下:
加載和存儲指令用於將數據在棧幀中的局部變量表和操做數棧之間來回傳輸,指令內容以下:
記錄常量指令的區別:
當int取值-1~5時,JVM採用iconst指令將常量壓入棧中。
當int取值-128~127時,JVM採用bipush指令將常量壓入棧中。
當int取值-32768~32767時,JVM採用sipush指令將常量壓入棧中。
當int取值-2147483648~2147483647時,JVM採用ldc指令將常量壓入棧中。
存儲數據的操做數棧和局部變量表主要就是由加載和存儲指令進行操做,除此以外,還有少許指令,如訪問對象的字段或者數組元素的指令也會向操做數棧傳輸數據。
上述的_<n>表述一組數據,如iload_<n>,它表明了 iload_0、iload_一、iload_2 和 iload_3 這幾條指令。iload_0的語義與操做數棧數爲0時的iload指令語義徹底一致。
運算指令用於對兩個操做數棧上的值進行某種特定特定運算,並把結果從新存儲到操做棧頂。對於運算指令而言能夠分紅兩種:** 對整形數據進行運算的指令與對浮點型數據進行運算的指令。 **不管哪一種運算指令,都是用Java虛擬機的數據類型,因爲沒有直接支持byte,short,char和boolean的運算指令,對於此類的運算,使用操做int類型的指令代替。運算指令以下:
類型轉換指令可讓兩種不一樣的數值類型進行互相轉換。Java虛擬機直接支持如下數值類型的寬化類型轉換(即小範圍到大範圍的轉換):
對於窄話類型轉換,須要顯示使用轉換指令完成,指令包括:i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l,d2f。對於窄化類型轉換結果會致使轉換結果產生不一樣的正負號,精度丟失的狀況,好比float轉int。
雖然類實例和數組都是對象,可是Java虛擬機對於這兩種的建立和操做使用了不一樣的字節碼指令,對象建立後,就能夠經過對象訪問指令獲取對象實例或者數組實例中的字段或者數組元素。對應指令以下:
Java虛擬機提供了一下直接操做操做數棧的指令,包括:
控制轉移指令就是在有條件或者無條件修改pc寄存器的值,即代碼中的if,else,switch等關鍵字,指令以下:
方法返回指令是根據返回值的類型區分的,包括ireturn(返回值是boolean,byte,char,short和 int),lreturn,freturn,drturn和areturn,另一個return供void方法,實例初始化方法,類和接口的類初始化i方法使用。
在Java程序中顯式拋出異常的操做(throw語句)都有athrow 指令來實現,除了用throw 語句顯示拋出異常狀況外,Java虛擬機規範還規定了許多運行時異常會在其餘Java虛擬機指令檢測到異常情況時自動拋出。 在Java虛擬機中,處理異常不是由字節碼指令來實現的,而是採用異常表來完成的。
方法級的同步是隱式的,無需經過字節碼指令來控制,它實如今方法調用和返回操做中。虛擬機從方法常量池中的方法標結構中的 ACC_SYNCHRONIZED標誌區分是不是同步方法。方法調用時,調用指令會檢查該標誌是否被設置,若設置,執行線程先成功持有管程,而後才能執行方法,最後方法完成釋放管程。
若是在同步方法執行期間,方法拋了異常,而且在內部沒法處理異常,這個同步方法所持有的管程將會在異常拋到外部時自動釋放。
同步一段指令集序列,一般由synchronized塊標示,JVM指令集中有monitorenter和monitorexit來支持synchronized語義。 結構化鎖定是指方法調用期間每個monitor退出都與前面monitor進入相匹配的情形。
總結:
這裏只是學習了一下理論的知識,弄明白了對應指令作的對應的事情,經過javap反編譯出來的文件中就包含了對應的指令:
public int testInt(java.lang.Object); descriptor: (Ljava/lang/Object;)I flags: ACC_PUBLIC Code: stack=2, locals=7, args_size=2 0: aload_1 1: dup 2: astore_2 3: monitorenter 4: bipush 11 6: istore_3 7: bipush 22 9: istore 4 11: aload_1 12: invokevirtual #12 // Method java/lang/Object.hashCod :()I 15: iconst_2 16: irem 17: ifne 27 20: bipush 33 22: istore_3 23: bipush 44 25: istore 4 27: iload_3 28: iload 4 30: iadd 31: istore 5 33: iload 5 35: aload_2 36: monitorexit 37: ireturn 38: astore_3 39: iconst_0 40: aload_2 41: monitorexit 42: ireturn 43: astore 6 45: aload_2 46: monitorexit 47: aload 6 49: athrow
在以後的學習中,須要應用的對應的分析當中,明白上述的執行過程。