Java虛擬機(jī)顧名思義,就是一臺虛擬的機(jī)器,而字節(jié)碼(bytecode)就是運(yùn)行在這臺虛擬機(jī)器上的機(jī)器碼。
每一個類或者接口都會被Java編譯器編譯成一個class文件,類或接口的方法信息就放在class文件的method_info結(jié)構(gòu)中 商佑。
如果方法不是抽象的,也不是本地方法厢塘,方法的Java代碼就會被編譯器編譯成字節(jié)碼(即使方法是空的茶没,編譯器也會生成一條return語句),存放在method_info結(jié)構(gòu)的Code屬性中晚碾。
package jvmgo.book.ch03;
public class ClassFileTest {
public static final boolean FLAG = true;
public static final byte BYTE = 123;
public static final char X = 'X';
public static final short SHORT = 12345;
public static final int INT = 123456789;
public static final long LONG = 12345678901L;
public static final float PI = 3.14f;
public static final double E = 2.71828;
public static void main(String[] args) throws RuntimeException {
System.out.println("Hello, World!");
}
}
字節(jié)碼中存放編碼后的Java虛擬機(jī)指令抓半。每條指令都以一個單字節(jié)的操作碼(opcode)開頭,這就是字節(jié)碼名稱的由來格嘁。由于只使用一字節(jié)表示操作碼笛求,顯而易見,Java虛擬機(jī)最多只能支持256(2^8 )條指令糕簿。
到第八版為止探入,Java虛擬機(jī)規(guī)范已經(jīng)定義了205條指令,操作碼分別是0(0x00)到202(0xCA)懂诗、254(0xFE)和255(0xFF)蜂嗽。
這205條指令構(gòu)成了Java虛擬機(jī)的指令集(instruction set)。
和匯編語言類似殃恒,為了便于記憶植旧,Java虛擬機(jī)規(guī)范給每個操作碼都指定了一個助記符(mnemonic)。比如操作碼是0x00這條指令芋类,因為它什么也不做隆嗅,所以它的助記符是nop(no operation)界阁。
Java虛擬機(jī)使用的是變長指令侯繁,操作碼后面可以跟零字節(jié)或多字節(jié)的操作數(shù)(operand)。如果把指令想象成函數(shù)的話泡躯,操作數(shù)就是它的參數(shù)贮竟。為了讓編碼后的字節(jié)碼更加緊湊,很多操作碼本身就隱含了操作數(shù)较剃,比如把常數(shù)0推入操作數(shù)棧的指令是iconst_0咕别。
可以看到,該指令的操作碼是0xB2写穴,助記符是getstatic惰拱。
它的操作數(shù)是0x0002,代表常量池里的第二個常量啊送。
在第4章中討論過偿短,操作數(shù)棧和局部變量表只存放數(shù)據(jù)的值欣孤,并不記錄數(shù)據(jù)類型。
結(jié)果就是:指令必須知道自己在操作什么類型的數(shù)據(jù)昔逗。這一點(diǎn)也直接反映在了操作碼的助記符上降传。
例如,
iadd
指令就是對int
值進(jìn)行加法操作勾怒;
dstore
指令把操作數(shù)棧頂?shù)?code>double值彈出婆排,存儲到局部變量表中;
areturn
從方法中返回引用
值笔链。
也就是說段只,如果某類指令可以操作不同類型的變量,則助記符的第一個字母表示變量類型鉴扫。
Java虛擬機(jī)規(guī)范把已經(jīng)定義的205條指令按用途分成了11類:
- 常量(constants)指令:
把常量推入操作數(shù)棧頂翼悴。常量可以來自三個地方:隱含在操作碼里、操作數(shù)和運(yùn)行時常量池幔妨。常量指令共有21條
加載(loads)指令鹦赎、
存儲(stores)指令、
操作數(shù)棧(stack)指令误堡、
數(shù)學(xué)(math)指令古话、
轉(zhuǎn)換(conversions)指令、
比較(comparisons)指令锁施、
控制(control)指令陪踩、
引用(references)指令、
擴(kuò)展(extended)指令
保留(reserved)指令悉抵。
保留指令一共有3條肩狂。其中一條是留給調(diào)試器的,用于實現(xiàn)斷點(diǎn)姥饰,操作碼是202(0xCA)傻谁,助記符是
breakpoint
。
另外兩條留給Java虛擬機(jī)實現(xiàn)內(nèi)部使用列粪,操作碼分別是254(0xFE)和266(0xFF)审磁,助記符是impdep1
和impdep2
。
這三條指令不允許出現(xiàn)在class文件中岂座。
5.2 指令和指令解碼
Java虛擬機(jī)規(guī)范的2.11節(jié)介紹了Java虛擬機(jī)解釋器的大致邏
輯
do {
atomically calculate pc and fetch opcode at pc;
if (operands) fetch operands;
execute the action for the opcode;
} while (there is more to do);
每次循環(huán)都包含三個部分:
計算pc态蒂、指令解碼、指令執(zhí)行费什。
go偽代碼
for {
pc := calculatePC()
opcode := bytecode[pc]
inst := createInst(opcode)
inst.fetchOperands(bytecode)
inst.execute()
}