描述
本文摘自深入理解Java虛擬機(jī)中關(guān)于字節(jié)碼的介紹,部分指令參考欢嘿,oracle字節(jié)碼指令集衰琐。
數(shù)據(jù)類型:byte、short炼蹦、int羡宙、long、float掐隐、double狗热、char、boolean虑省、reference匿刮。操作碼以首字符為前綴,reference以a為前綴探颈。例如iload熟丸,加載int到棧頂、aload加載reference到棧頂伪节。
加載和存儲(chǔ)指令
加載和存儲(chǔ)指令用于將數(shù)據(jù)在棧幀中的局部變量表和操作數(shù)棧之間來(lái)回傳輸光羞。
- 將一個(gè)局部變量加載到操作棧:iload、iload_<n>怀大、lload纱兑、lload_<n>、aload化借、aload_<n>
- 將一個(gè)數(shù)值從操作數(shù)棧存儲(chǔ)到局部變量表:istore萍启、istore_<n>、lstore屏鳍、lstore_<n>、astore局服、astore_<n>
- 將一個(gè)常量加載到操作數(shù)棧:bipush钓瞭、sipush、aconst_null淫奔、iconst_<i>山涡、ldc
- 擴(kuò)充局部變量表的訪問(wèn)索引的指令:wide
<n>:如iload_2,指從局部變量表下標(biāo)2處整型元素加載到操作數(shù)棧頂。iload_0和iload語(yǔ)義一致
<i>:如iconst_2鸭丛,指從整型常量池中常量2加載到操作數(shù)棧頂
bipush:將byte整型常量值推入操作數(shù)棧頂
sipush:將short整型常量值推入操作數(shù)棧頂
ldc:將int竞穷、float、String型常量值從常量池中推入至棧頂
運(yùn)算指令
由于Java虛擬機(jī)沒有直接支持byte鳞溉、short瘾带、char、boolean類型的算術(shù)指令熟菲,使用操作int類型指令替代看政。
- 加法:iadd、ladd
- 減法:isub抄罕、lsub
- 乘法:imul允蚣、lmul
- 除法:idiv、ldiv
- 求余:irem呆贿、lrem
- 取反:ineg嚷兔、lneg
- 局部變量自增:iinc
類型轉(zhuǎn)換指令
小范圍轉(zhuǎn)大范圍無(wú)需顯示轉(zhuǎn)換
- int到long、float做入、double
- long到float冒晰、double
- float到double
大范圍到小范圍需使用指令
- i2b:將棧頂int型數(shù)值強(qiáng)制轉(zhuǎn)換成byte型數(shù)值并將結(jié)果壓入棧頂
- f2l:將棧頂float型數(shù)值強(qiáng)制轉(zhuǎn)換成int型數(shù)值并將結(jié)果壓入棧頂
- d2l:將棧頂double型數(shù)值強(qiáng)制轉(zhuǎn)換成long型數(shù)值并將結(jié)果壓入棧頂
對(duì)象創(chuàng)建與訪問(wèn)指令
- 創(chuàng)建類實(shí)例:new
- 創(chuàng)建數(shù)組:newarray
- 訪問(wèn)類字段(static字段)和實(shí)例字段(非static字段):getfield、putfield母蛛、getstatic翩剪、putstatic
- 把一個(gè)數(shù)組元素加載到操作數(shù)棧:iaload、aaload彩郊、daload
- 把一個(gè)操作數(shù)棧的值存儲(chǔ)到數(shù)組元素中:iastore前弯、aastore、daload
- 取數(shù)組長(zhǎng)度:arraylength
操作數(shù)棧管理指令
- 將操作數(shù)棧的棧頂一個(gè)或兩個(gè)元素出棧:pop秫逝、pop2
- 復(fù)制棧頂一個(gè)或兩個(gè)數(shù)值并將復(fù)制值或雙份的復(fù)制值重新壓入棧頂:dup恕出、dup2
- 將棧頂?shù)膬蓚€(gè)數(shù)值互換:swap
控制轉(zhuǎn)移指令
控制轉(zhuǎn)移指令就是在有條件或無(wú)條件地修改PC寄存器的值
- 條件分支:ifeq、iflt违帆、浙巫、ifle、ifgt刷后、ifge的畴、ifnull、ifnonnnull
- 復(fù)合條件分支:tableswitch尝胆、lookupswitch
方法調(diào)用和返回指令
- invokevirtual:調(diào)用對(duì)象方法
- invokeinterface:調(diào)用接口方法丧裁,運(yùn)行時(shí)搜索一個(gè)實(shí)現(xiàn)了這個(gè)接口方法的對(duì)象,找出適合的方法進(jìn)行調(diào)用
- invokespecial:調(diào)用需要特殊處理的方法含衔,如構(gòu)造器煎娇、私有方法二庵、父類方法
- invokestatic:調(diào)用類方法(static方法)
異常處理指令
采用異常表實(shí)現(xiàn)
- athrow:將棧頂異常拋出
同步指令
方法級(jí)同步是隱式的,訪問(wèn)標(biāo)志ACC_SYNCHRONIZED缓呛。同步一段代碼是由Java虛擬機(jī)指令集monitorenter和monitorexit來(lái)支持synchronized關(guān)鍵字語(yǔ)義催享。
示例代碼
public class ByteCodeDemo {
public static void main(String[] args) {
ByteCodeDemo demo = new ByteCodeDemo();
demo.sayHello();
}
public void sayHello() {
synchronized (this) {
System.out.println("hello");
}
}
public synchronized void sayGoodBye() {
System.out.println("good bye");
}
}
使用javap -verbose ByteCodeDemo.class
解析字節(jié)碼。對(duì)于私有方法不解析
public void sayHello();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #6 // String hello
9: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_1
13: monitorexit
14: goto 22
17: astore_2 // 存儲(chǔ)異常信息到局部變量表
18: aload_1
19: monitorexit
20: aload_2 // 加載異常信息
21: athrow // 將棧頂異常拋出
22: return
Exception table:
from to target type
4 14 17 any // 4-14行指令拋出任何異常就執(zhí)行17行指令
17 20 17 any // 17-20行指令拋出任何異常就執(zhí)行17行指令
public synchronized void sayGoodBye();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=1, args_size=1
0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #8 // String good bye
5: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return