虛擬機的概念
首先問一個基本的問題,作爲一個虛擬機,它最基本的要實現哪些功能?
他應該能夠模擬物理CPU對操作數的移進移出,理想狀態下,它應該包含如下概念:(1)將源碼編譯成VM指定的字節碼。
(2)包含指令和操作數的數據結構(指令用於處理操作數作何種運算)。
(3)一個爲所有函數操作的調用棧。
(4)一個“指令指針(Instruction Point ---IP)”:用於指向下一條將要執行的指令。
(5)一個虛擬的“CPU”--指令的派發者:
1)取指:獲取下一條指令(通過IP獲取)
2)譯碼:對指令進行翻譯,將要作何種操作。
3)執行:執行指令。
以上是CPU的三級流水線操作,實際上五級流水線還包括回寫,即把執行後生成的結果回寫進存儲器。
有兩種基本的方法實現虛擬機:基於Stack的和基於Register的,比如基於Stack的虛擬機有JVM、.net的CLR,這種基於Stack實現虛擬機是一種廣泛的實現方法。
而基於Register的虛擬機有Lua VM(是Lau編程語言的虛擬機)和Dalvik VM。這兩種虛擬機實現的不同主要在於操作數和結果的存儲和檢索機制不一樣。
Stack-Based虛擬機
一個基於Stack的虛擬機會通過IP來獲取操作數,其操作數都是保存在Stack數據結構中,從棧中取出數據、計算然後再將結果存入棧中(LIFO,Last in first out)。如下就是一個典型的計算20+7在棧中的計算過程:
2、POP 7
3、ADD 20, 7, result
4、PUSH result
Register-Based虛擬機
基於寄存器的虛擬機,它們的操作數是存放在CPU的寄存器的。沒有入棧和出棧的操作和概念。但是執行的指令就需要包含操作數的地址了,也就是說,指令必須明確的包含操作數的地址,這不像棧可以用棧指針去操作。比如如下的加法操作:
正如前面所說,基於寄存器的VM沒有入棧和出棧的操作。所以加法指令只需要一行就夠了,但是不像Stack-Based一樣,我們需要明確的制定操作數R1、R2、R3(這些都是寄存器)的地址。這種設計的有點就是去掉了入棧和出棧的操作,並且指令在寄存器虛擬機執行得更快。
基於寄存器得虛擬機還有一個優點就是一些在基於Stack的虛擬機中無法實現的優化,比如,在代碼中有一些相同的減法表達式,那麼寄存器只需要計算一次,然後將結果保存,如果之後還有這種表達式需要計算就直接返回結果。這樣就減少了重複計算所造成的開銷。
當然,寄存器虛擬機也有一些問題,比如虛擬機的指令比Stack vm指令要長(因爲Register指令包含了操作數地址)。