字节码指令
四、类型检查指令
检查类实例或数组类型的指令:instanceof、checkcast.
·指令checkcast用于检查类型强制转换是否可以进行。如果可以进行,那么checkcast指令不会改变操作数栈,否则它会抛出ClassCastException异常。
·指令instanceof用来判断给定对象是否是某一个类的实例,它会将判断结果压入操作数栈。
方法调用指令
: invokevirtual、invokeinterface、invokespecial、invokestatic . invokedynamic
以下5条指令用于方法调用:
invokevirtual指令用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派),支持多态。这也是Java语言中最常见的方法分派方式。
invokeinterface指令用于调用接口方法,它会在运行时搜索由特定对象所实现的这个接口方法,并找出适合的方法进行调用。
invokespecial指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法(构造器)、私有方法和父类方法。这些方法都是静态类型绑定的,不会在调用时进行动态派发。
. invokestatic指令用于调用命名类中的类方法(static方法。这是静态绑定的。
invokedynamic:调用动态绑定的方法,这个是JDK 1.7后新加入的指令。用于在运行时动态解析出调用点限定符所引用的方法,并执行该方法。前面4条调用指令的分派逻辑都固化在 java虚拟机内部,而
invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的。
代码实例
//方法调用指令:invokespecial:静态分派
public void invoke1(){
//情况1:类实例构造器方法:<init>()
Date date = new Date();
Thread t1 = new Thread();
//情况2:父类的方法
super.toString();
//情况3:私有方法
methodPrivate();
}
private void methodPrivate(){
}
//字节码 // // // // // // //
0 new #2 <java/util/Date>
3 dup
4 invokespecial #3 <java/util/Date.<init>>
7 astore_1
8 new #4 <java/lang/Thread>
11 dup
12 invokespecial #5 <java/lang/Thread.<init>>
15 astore_2
16 aload_0
17 invokespecial #6 <java/lang/Object.toString>
20 pop
21 aload_0
22 invokespecial #7 <main/likou/MethodInvokeReturnTest.methodPrivate>
25 return
//方法调用指令:invokestatic:静态分派
public void invoke2(){
methodStatic();
}
public static void methodStatic(){
}
//字节码//字节码//字节码//字节码//字节码//字节码//字节码
0 invokestatic #8 <main/likou/MethodInvokeReturnTest.methodStatic>
3 return
//invokeinterface和invokestaticy优先考虑后者
//方法调用指令:invokeinterface
public void invoke3(){
Thread t1 = new Thread();
((Runnable)t1).run();
Comparable<Integer> com = null;
com.compareTo(123);
}
//字节码//字节码//字节码//字节码//字节码//字节码//字节码
0 new #4 <java/lang/Thread>
3 dup
4 invokespecial #5 <java/lang/Thread.<init>>
7 astore_1
8 aload_1
9 invokeinterface #9 <java/lang/Runnable.run> count 1
14 aconst_null
15 astore_2
16 aload_2
17 bipush 123
19 invokestatic #10 <java/lang/Integer.valueOf>
22 invokeinterface #11 <java/lang/Comparable.compareTo> count 2
27 pop
28 return
//方法调用指令:invokeVirtual:动态分派
public void invoke4(){
System.out.println("hello");
Thread t1 = null;
t1.run();
}
//字节码//字节码//字节码//字节码//字节码//字节码//字节码
0 getstatic #12 <java/lang/System.out>
3 ldc #13 <hello>
5 invokevirtual #14 <java/io/PrintStream.println>
8 aconst_null
9 astore_1
10 aload_1
11 invokevirtual #15 <java/lang/Thread.run>
14 return
方法返回指令
方法调用结束前,需要进行返回。方法返回指令是根据返回值的类型区分的。包括ireturn(当返回值是 boolean、byte、char、short和int类型时使用)、lreturn、freturn、dreturn和areturnI
·另外还有一条return指令供声明为 void的方法、实例初始化方法以及类和接口的类初始化方法使用。
举例:
通过ireturn指令,将当前函数操作数栈的顶层元素弹出,并将这个元素压入调用者函数的操作数栈中(因为调用者非常关心函数的返回值),所有在当前函数操作数栈中的其他元素都会被丢弃。
如果当前返回的是synchronized方法,那么还会执行一个隐含的monitorexit指令,退出临界区。
最后,会丢弃当前方法的整个帧,恢复调用者的帧,并将控制权转交给调用者。
public byte returnByte(){
return 0;
}
public void methodReturn(){
int i = returnByte();
}
//字节码//字节码//字节码//字节码//字节码//字节码//字节码//字节码//字
0 iconst_0
1 ireturn
0 aload_0
1 invokevirtual #17 <main/likou/MethodInvokeReturnTest.returnByte>
4 istore_1
5 return
操作数栈管理指令
详细教程
如同操作一个普通数据结构中的堆栈那样,JVM提供的操作数栈管理指令,可以用于直接操作操作数栈的指令。
这类指令包括如下内容:
·将一个或两个元素从栈顶弹出,并且直接废弃:pop, pop2;
·复制栈顶一个或两个数值并将复制值或双份的复制值重新压入栈顶: dup,dup2,dup_x1,dup2_x1,dup_x2,dup2_x2;
·将栈最顶端的两个Slot数值位置交换:swap。Java虚拟机没有提供交换两个64位数据类型(long、double)数值的指令。
·指令nop,是一个非常特殊的指令,它的字节码为0x00。和汇编语言中的nop一样,它表示什么都不做。这条指令一般可用于调试、占位等。
这些指令属于通用型,对栈的压入或者弹出无需指明数据类型。
说明;
·不带_x的指令是复制栈顶数据并压入栈顶。包括两个指令dup和dup2.dup的系数代表要复制的slot个数。
.dup开头的指令用于复制1个Slot的数据。例如1个int或1个reference类型数据
.dup2开头的指令用于复制2个Slot的数据。例如1个long,或2个int,或1个int+1个float类型数据
·带_x的指令是复制栈顶数据并插入栈顶以下的某个位置。共有4个指令, dup_x1, dup2_x1,dup_x2,dup2_x2.对于带_x的复制插入指令,只要将指令的dup和x的系数相加,结果即为需要插入的位置。因此
. dup_x1插入位置:1+1=2,即栈顶2个Slot下面.
dup_x2插入位置:1+2=3,即栈顶3个S1ot下面.
dup2_x1插入位置:2+1=3,即栈顶3个S1ot下面.
dup2_×2插入位置:2+2=4,即栈顶4个Slot下面
pop:将栈顶的1个slot数值出栈。例如1个short类型数值
pop2:将栈顶的2个Slot数值出栈。例如1个pouble类型数值,或者2个int类型数值
测试代码
public class StackOperateTest {
public void print(){
Object obj = new Object();
// String info = obj.toString();
obj.toString();
}
//类似的
public void foo(){
bar();
}
public long bar(){
return 0;
}
public long nextIndex() {
return index++;
}
private long index = 0;
}
/字节码
//1.print
0 new #3 <java/lang/Object>
3 dup
4 invokespecial #1 <java/lang/Object.<init>>
7 astore_1
8 aload_1
9 invokevirtual #4 <java/lang/Object.toString>
12 pop
13 return
//2.foo
0 aload_0
1 invokevirtual #5 <main/likou/StackOperateTest.bar>
4 pop2
5 return
//