基于棧的指令集與基于寄存器的指令集
Javac編譯器輸出的字節(jié)碼指令流毫缆,基本上是一種基于棧的指令集架構(gòu)(Instruction Set Architecture,ISA),字節(jié)碼指令流里面的指令大部分都是零地址指令梆掸,它們依賴(lài)操作數(shù)棧進(jìn)行工作痕慢。
與之相對(duì)的另外一套常用的指令集架構(gòu)師基于寄存器的指令集,最典型的就是x86的二地址指令集狂秘,通俗來(lái)講就是現(xiàn)在主流PC機(jī)中物理硬件直接支持的指令集架構(gòu)磅甩,這些指令依賴(lài)寄存器進(jìn)行工作。
兩者之間的區(qū)別:
以“1+1”為例绩社,
- 基于棧的指令集如下
iconst_1
iconst_1
iadd
istore_0
兩條iconst_1指令連續(xù)把另個(gè)常量1壓入棧后,iadd指令把棧頂?shù)膬蓚€(gè)值出棧赂苗、相加愉耙。然后把結(jié)果放回棧頂,最后istore_0把棧頂?shù)闹捣诺骄植孔兞勘淼牡?個(gè)變量槽中拌滋。
這種指令流中的指令通常都是不帶參數(shù)的朴沿,使用操作數(shù)棧中的數(shù)據(jù)作為指令的運(yùn)算輸入,指令的運(yùn)算結(jié)果也存儲(chǔ)在操作數(shù)棧之中败砂。
- 基于寄存器的指令集
mov eax,1
add eax,1
mov指令把EAX寄存器的值設(shè)為1赌渣,然后add指令再把這個(gè)值加1,結(jié)果就保存在EAX寄存器里昌犹。這種二地址指令是x86指令集中的主流坚芜,每個(gè)指令都包含兩個(gè)單獨(dú)的輸入?yún)?shù),依賴(lài)于寄存器來(lái)訪(fǎng)問(wèn)和存儲(chǔ)數(shù)據(jù)斜姥。
基于棧的指令集主要優(yōu)點(diǎn)是可移植鸿竖,因?yàn)榧拇嫫饔捎布苯犹峁绦蛑苯右蕾?lài)這些硬件寄存器則不可避免要受到硬件約束铸敏。如現(xiàn)在的32位80x86體系的處理器能提供了8個(gè)32位的寄存器缚忧,而ARMv6體系的處理器則提供了30個(gè)32位的通用寄存器,其中前16個(gè)用戶(hù)模式可使用杈笔。如使用棧架構(gòu)的指令集闪水,用戶(hù)程序不會(huì)直接用到這些寄存器,那就可由虛擬機(jī)實(shí)現(xiàn)來(lái)自行決定把一些訪(fǎng)問(wèn)最頻繁的數(shù)據(jù)(程序計(jì)數(shù)器蒙具、棧頂緩存等)放到寄存器中以獲取盡量好的性能球榆,這樣實(shí)現(xiàn)起來(lái)也更簡(jiǎn)單一些峰弹。棧機(jī)構(gòu)的指令集還有一些其它優(yōu)點(diǎn),如代碼相對(duì)更加緊湊(字節(jié)碼每個(gè)字節(jié)就對(duì)應(yīng)一條指令)芜果、編譯器實(shí)現(xiàn)更加簡(jiǎn)單(不需考慮空間分配)等。
主要缺點(diǎn)是理論上執(zhí)行速度相對(duì)來(lái)說(shuō)會(huì)稍慢一些融师,所有主流物理機(jī)的指令集都是寄存器架構(gòu)也從側(cè)面印證了這點(diǎn)右钾。不過(guò)這里的執(zhí)行速度就要局限于解析執(zhí)行的狀態(tài)下,如果經(jīng)過(guò)即時(shí)編譯器輸出成物理機(jī)上的匯編指令流旱爆,那就與虛擬機(jī)采用哪種指令的架構(gòu)沒(méi)什么關(guān)系舀射。
在解釋執(zhí)行時(shí),棧架構(gòu)指令集的代碼雖然緊湊怀伦,但完成相同功能所需的指令數(shù)量一般會(huì)比寄存器架構(gòu)來(lái)的更多脆烟。因?yàn)槌鰲!⑷霔2僮鞅旧砭彤a(chǎn)生了相當(dāng)大量的指令房待。更重要的是棧實(shí)現(xiàn)的內(nèi)存中邢羔,頻繁的棧訪(fǎng)問(wèn)也就意味著頻繁的內(nèi)存訪(fǎng)問(wèn),相對(duì)于處理器來(lái)說(shuō)桑孩,內(nèi)存始終是執(zhí)行速度的瓶頸拜鹤。盡管虛擬機(jī)可采取棧頂緩存的優(yōu)化方法,把最常用的操作映射到寄存器中避免直接內(nèi)存訪(fǎng)問(wèn)流椒,但這也只是優(yōu)化措施而不是解決本質(zhì)問(wèn)題的方法敏簿。因此由于指令數(shù)量和內(nèi)存訪(fǎng)問(wèn)的原因,導(dǎo)致了棧架構(gòu)指令集的執(zhí)行速度會(huì)相對(duì)慢上一些宣虾。
《深入理解Java虛擬機(jī)》第三版學(xué)習(xí)