jvm原理(35)基於棧的指令集與基於寄存器的指令集詳細比對&執行棧指令集實例剖析

基於棧的指令集與基於寄存器的指令集詳細比對

現代JVM在執行Java代碼的時候,通常都會將解釋執行與編譯執行二者結合起來進行。

所謂解釋之行,就是通過解釋器來讀取字節碼,遇到相應的指令就去執行該指令。
所謂編譯執行,就是通過即時編譯器(just in time jit)將字節碼轉換爲本地機器碼執行,現代jvm會根據代碼熱點生成相應的本地機器碼。

執行的方式有基於棧的和基於寄存器的執行方式:
基於棧: 移植性好,java是基於棧的指令集,爲了可移植性。由於棧是在內存裏邊出棧入棧,相比cpu寄存器,速度比較慢。
完成相同的操作,基於棧的指令集要比基於寄存器的指令集所需要的指令數量要多。
基於寄存器: 寄存器和硬件架構綁定在一塊,因此移植性不ok,但是執行速度快。基於寄存器的指令集是在寄存器裏邊執行的,速度很快。
雖然虛擬機可以採取一些優化手段,但總體來說,基於棧的指令集的執行速度要慢一些。

比如我們要運算2-1,就會有如下的入棧出棧操作:

  1. iconst_1 //1入棧
  2. iconst_2 //2入棧
  3. isub //(1)將棧頂元素出棧,棧頂元素下邊的元素出棧;(2)棧頂元素減去棧頂下邊的元素;(3)將結果放入棧頂; isub完成了是3個操作。
  4. istore_0 //將結果放在slot0處,slot0處可能是一個變量,方法返回的時候,可以把這個變量返回。

如果是基於寄存器去運算這個減法,第一步就是把2放到一個寄存器上,然後cpu執行減一,然後把結果直接放在原來的寄存器上。過程很簡單。

執行棧指令集實例剖析

編寫一個很簡單的程序:

package com.twodragonlake.jvm.bytecode;
public class MyTest8 {
    public int myCalulate(){
        int a =1;
        int b =2;
        int c =3;
        int d =4;

        int result = (a + b - c) * d;
        return result;
    }
}

javap反編譯:
javap -verbose -p com/twodragonlake/jvm/bytecode.MyTest8

找到myCalulate的字節碼:

Code:
  stack=2, locals=6, args_size=1 //棧最大深度是2;棧最大的局部變量是6個;myCalulate方法的參數個數是1個(this)
     0: iconst_1 //將1入棧
     1: istore_1 //將1出棧,然後將1放在slot索引爲1的位置的slot上(slot索引爲0是this)
     2: iconst_2 //將2入棧
     3: istore_2 //將2出棧,然後將2放到slot索引爲2的slot位置上
     4: iconst_3 //將3入棧
     5: istore_3 //將3出棧,然後放在索引爲3的slot上
     6: iconst_4 //將4入棧
     7: istore        4  //將4出棧,然後放到索引爲4的slot上,注意不是istore_4 因爲istore_X最多到istore_3
     目前的狀態如下:
         棧                 方法的局部變量表   
     ===========            ==============
     |         |            |     this    |
     -----------            ---------------
     |         |            |      1      |
     -----------            ---------------
                            |      2      |
                            ---------------
                            |      3      |
                            ---------------
                            |      4      |
                            ---------------   
                            |             |
                            ---------------                             

     9: iload_1 //將局部變量表索引爲1的元素的值,推到棧頂
    10: iload_2 //將局部變量表索引爲2的元素的值,推到棧頂
     目前的狀態如下:
    棧                 方法的局部變量表   
===========            ==============
|    2    |            |     this    |
-----------            ---------------
|    1    |            |      1      | 已入棧
-----------            ---------------
                       |      2      | 已入棧
                       ---------------
                       |      3      |
                       ---------------
                       |      4      |
                       ---------------   
                       |             |
                       ---------------  

    11: iadd //將2和1彈出棧,然後相加(2+1=3),得到結果3,將三壓入棧頂
    棧                 方法的局部變量表   
===========            ==============
|         |            |     this    |
-----------            ---------------
|    3    |            |      1      | 已入棧
-----------            ---------------
                       |      2      | 已入棧
                       ---------------
                       |      3      |
                       ---------------
                       |      4      |
                       ---------------   
                       |             |
                       ---------------  

    12: iload_3 //將局部變量表索引爲3的位置的元素推送到棧頂
    棧                 方法的局部變量表   
===========            ==============
|    3    |            |     this    |
-----------            ---------------
|    3    |            |      1      | 已入棧
-----------            ---------------
                       |      2      | 已入棧
                       ---------------
                       |      3      | 已入棧
                       ---------------
                       |      4      |
                       ---------------   
                       |             |
                       ---------------  
    13: isub    //將棧頂元素彈出,相減(3-3=0),得到結果推送到棧頂
    棧                 方法的局部變量表   
===========            ==============
|    0    |            |     this    |
-----------            ---------------
|         |            |      1      | 已入棧
-----------            ---------------
                       |      2      | 已入棧
                       ---------------
                       |      3      | 已入棧
                       ---------------
                       |      4      |
                       ---------------   
                       |             |
                       ---------------
    14: iload         4 //將局部變量表索引爲4的變量的值推送到棧頂
    棧                 方法的局部變量表   
===========            ==============
|    4    |            |     this    |
-----------            ---------------
|    0    |            |      1      | 已入棧
-----------            ---------------
                       |      2      | 已入棧
                       ---------------
                       |      3      | 已入棧
                       ---------------
                       |      4      | 已入棧
                       ---------------   
                       |             |
                       ---------------
    16: imul //棧頂2個元素彈出,執行乘法(0*4=0),得到的結果推送到棧頂
    棧                 方法的局部變量表   
===========            ==============
|    0    |            |     this    |
-----------            ---------------
|         |            |      1      | 已入棧
-----------            ---------------
                       |      2      | 已入棧
                       ---------------
                       |      3      | 已入棧
                       ---------------
                       |      4      | 已入棧
                       ---------------   
                       |             |
                       ---------------
    17: istore        5  //棧頂元素出棧,然後將索引爲5的slot的設置的值爲棧頂元素。就是局部變量表5的位置賦值爲棧頂元素
    棧                 方法的局部變量表   
===========            ==============
|         |            |     this    |
-----------            ---------------
|         |            |      1      | 已入棧
-----------            ---------------
                       |      2      | 已入棧
                       ---------------
                       |      3      | 已入棧
                       ---------------
                       |      4      | 已入棧
                       ---------------   
                       |      0      |
                       ---------------
    19: iload         5  //將局部變量表5的位置推送到棧頂
    棧                 方法的局部變量表   
===========            ==============
|    0    |            |     this    |
-----------            ---------------
|         |            |      1      | 已入棧
-----------            ---------------
                       |      2      | 已入棧
                       ---------------
                       |      3      | 已入棧
                       ---------------
                       |      4      | 已入棧
                       ---------------   
                       |      0      | 已入棧
                       ---------------
    21: ireturn  //方法將棧頂元素返回

iconst

第一個指令是iconst_1,我們到oracle的官方網站看一下他的說明:
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.iconst_i

iconst_
Operation
Push int constant
將一個常量入棧
Format
iconst_
Forms
iconst_m1 = 2 (0x2) //-1入棧
iconst_0 = 3 (0x3) //0入棧
iconst_1 = 4 (0x4) //…
iconst_2 = 5 (0x5)
iconst_3 = 6 (0x6)
iconst_4 = 7 (0x7)
iconst_5 = 8 (0x8)

Operand Stack
… →

…,

Description
Push the int constant (-1, 0, 1, 2, 3, 4 or 5) onto the operand stack.
將(-1, 0, 1, 2, 3, 4 or 5)壓入到棧頂

Notes
Each of this family of instructions is equivalent to bipush for the respective value of , except that the operand is implicit.
每個指令等價於bipush 就等於iconst後邊的數字,只不過操作數是隱式的。

istore

istore指令,https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.istore_n
Operation
Store int into local variable
將一個整數村存儲到一個變量裏邊

Format
istore_

Forms
istore_0 = 59 (0x3b)
istore_1 = 60 (0x3c)
istore_2 = 61 (0x3d)
istore_3 = 62 (0x3e)

Operand Stack
…, value →

Description
The must be an index into the local variable array of the current frame (§2.6). The value on the top of the operand stack must be of type int. It is popped from the operand stack, and the value of the local variable at is set to value.
n 必須是局部變量表裏邊的一個索引,操作數棧的棧頂元素必須是整數類型,彈出棧頂元素,將這個元素放在局部變量n的位置

Notes
Each of the istore_ instructions is the same as istore with an index of , except that the operand is implicit.
每個istore_等價於 istore指令 帶上一個索引n,只不過n是隱式的。

Store

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.istore
Operation
Store int into local variable
將一個整型放到局部變量裏邊
Format

istore
index

Forms
istore = 54 (0x36)

Operand Stack
…, value →

Description
The index is an unsigned byte that must be an index into the local variable array of the current frame (§2.6). The value on the top of the operand stack must be of type int. It is popped from the operand stack, and the value of the local variable at index is set to value.

index必須是無符號(0–255)的,並且存在於局部變量表裏邊的一個索引,值是棧幀的頂部的元素,必須是整型的,將棧的頂部元素設置到局部變量表索引爲index的位置。

Notes
The istore opcode can be used in conjunction with the wide instruction (§wide) to access a local variable using a two-byte unsigned index.

iload

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.iload_n

Operation
Load int from local variable
從局部變量表加載一個整型數據
Format
iload_

Forms
iload_0 = 26 (0x1a)
iload_1 = 27 (0x1b)
iload_2 = 28 (0x1c)
iload_3 = 29 (0x1d)

Operand Stack
… →

…, value

Description
The must be an index into the local variable array of the current frame (§2.6). The local variable at must contain an int. The value of the local variable at is pushed onto the operand stack.
n必須是局部變量表的一個索引,值是一個整型類型,將索引n處的值push到棧頂
Notes
Each of the iload_ instructions is the same as iload with an index of , except that the operand is implicit.
每個iload_指令等價於 iload 跟上一個參數n,只不過iload_的n是隱式的。

iadd

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.iadd

Operation
Add int
整數相加
Format
iadd
Forms

iadd = 96 (0x60)

Operand Stack
…, value1, value2 →

…, result

Description
Both value1 and value2 must be of type int. The values are popped from the operand stack. The int result is value1 + value2. The result is pushed onto the operand stack.

The result is the 32 low-order bits of the true mathematical result in a sufficiently wide two’s-complement format, represented as a value of type int. If overflow occurs, then the sign of the result may not be the same as the sign of the mathematical sum of the two values.

Despite the fact that overflow may occur, execution of an iadd instruction never throws a run-time exception.

value1和value2必須是整型的,這些整數來自於操作數棧,即從操作數棧彈出來,整型的結果是value1加value2,然後將結果推送到棧頂。

結果是底位排序的整型類型,如果相加之後溢出,那麼得到的結果不是數學意義上的結果,儘管會有溢出的可能,但是即使溢出了,也不會拋出運行時異常

ireturn

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.ireturn

Operation
Return int from method
方法返回
Format

ireturn
Forms
ireturn = 172 (0xac)

Operand Stack
…, value →

[empty]

Description
The current method must have return type boolean, byte, short, char, or int. The value must be of type int. If the current method is a synchronized method, the monitor entered or reentered on invocation of the method is updated and possibly exited as if by execution of a monitorexit instruction (§monitorexit) in the current thread. If no exception is thrown, value is popped from the operand stack of the current frame (§2.6) and pushed onto the operand stack of the frame of the invoker. Any other values on the operand stack of the current method are discarded.
當前方法必須返回一個Boolean、 byte, short, char, 或者 int的類型的值,如果方法是synchronized修飾的,那麼進入monitor或者衝進入monitor,之後會有monitorexit就會方法退出,如果沒有異常,返回值就是棧頂彈出的元素,返回的元素在調用者的棧幀裏邊會被推送到棧頂,當前方法的棧幀裏邊的其他所有元素都會被丟棄掉。

The interpreter then returns control to the invoker of the method, reinstating the frame of the invoker.

Run-time Exceptions
If the Java Virtual Machine implementation does not enforce the rules on structured locking described in §2.11.10, then if the current method is a synchronized method and the current thread is not the owner of the monitor entered or reentered on invocation of the method, ireturn throws an IllegalMonitorStateException. This can happen, for example, if a synchronized method contains a monitorexit instruction, but no monitorenter instruction, on the object on which the method is synchronized.

Otherwise, if the Java Virtual Machine implementation enforces the rules on structured locking described in §2.11.10 and if the first of those rules is violated during invocation of the current method, then ireturn throws an IllegalMonitorStateException.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章