衆所周知,JVM以前一直採用的是解釋執行,但是後來在歷代的版本更迭中也加入了編譯執行。所以總的來說JVM是包含了解釋執行和編譯執行。這一部分不屬於JVM的範疇了,已經屬於編譯了,大多數都是進行詞法分析之類的,以後有時間會補充。
同時大家都知道現在大體上分爲兩種指令集架構,第一種就是基於棧的第二種是基於寄存器的,簡單點說,基於寄存器的架構速度更快,但是可移植性不強,但是基於棧的指令集架構雖然慢,但是可移植性很強,大家都知道java本身就是依靠可移植性出名的,所以無可爭議的使用了棧的指令集架構。(也有例外,dalvik是基於寄存器的)
下面我們詳述一下JVM的棧解釋器執行過程,在此之前我們先來講一下字節碼的指令含義:
加載和存儲:加載和存儲指令一般可以把棧幀中的局部變量放到操作數棧中,然後把操作數棧中的變量存回棧幀中。
把局部變量加載到操作數棧中:主要有iload,liload_<n>,lload,(每一個指令前面代表的是它操作的數據類型iload就是int lload就是long,接下來我們去除前面的前綴統一用x代替減少篇幅。)
從操作數棧中存回局部變量表 xstore_<n>,xstore(注意有的後面跟了_<n>這是省略了諸如xstore_1,xstore_2這樣的指令,xstore默認爲xstore_0,之後統一用xstore_<n>替代)
加載一個常量到操作數棧:xipush,xdc,xconst_<n>
運算指令:執行加減乘除取餘等運算
加:xadd
減:xsub
乘:xmul
除:xdiv
取餘:xrem
取反:xneg
位移:xshl。xshr
按位或指令:xor
按位與指令:xand
按位異或指令:xxor
比較指令:xcmpl
指令上我們大概就講這麼多,接下來就是我們查看字節碼的時刻了。首先寫一個方法如下:
public static void main(String[] args) {
// TODO Auto-generated method stub
int a = 2;
int b = 1;
int c = a+b;
System.out.println(c);
}
然後我們用javap查看一下字節碼
可以看到我們的操作從第01兩行來看這事int a =2;的操作,先放置常量2到棧頂然後取出來放到常量表中1的位置。23同樣。
而45這兩個數字就是把局部變量表上12這兩個位置的數加載到操作棧中,然後用6行相加存入常量表3的位置。
爲了表明0123和7真的實在存儲數字到常量表我們對方法做如下修改:
public static void main(String[] args) {
// TODO Auto-generated method stub
int c = 1+2;
System.out.println(c);
}
字節碼如下很明顯的之前的0123行那種存儲的行爲沒有了,同樣我們能看到javac給我們的優化,在第0行把1+2直接變成了3。