本篇为《JVM指令分析实例》的第五篇,相关实例均使用Oracle JDK 1.8编译,并使用javap生成字节码指令清单。
前几篇传送门:
JVM指令分析实例一(常量、局部变量、for循环)
JVM指令分析实例二(算术运算、常量池、控制结构)
JVM指令分析实例三(方法调用、类实例)
JVM指令分析实例四(数组、switch)
局部变量表的容量以变量槽(Variable Slot)为最小单位, 虚拟机规范中并没有明确指明一个Slot应占用的内存空间大小 。
每个Slot能存放一个boolean、byte、char、short、int、float、reference或returnAddress类型的数据。
reference类型表示对一个对象实例的引用, 虚拟机规范没有说明它的长度及结构 。
returnAddress类型目前已经很少见了,它是为字节码指令jsr、jsr_w和ret服务的,指向了一条字节码指令的地址。
对于64位的数据类型(long、double),虚拟机会以高位对齐的方式为其分配两个连续的Slot空间。
package jvm.specification.se8.chapter3; public class NextIndex { private long index = 0; public long nextIndex() { return index++; } }
public long nextIndex(): 0: aload_0 // 将第1个局部变量this压入栈顶 1: dup // 复制栈顶this并压入栈顶. 栈底到栈顶:this、this 2: getfield #12 // Field index:J. 获取实例字段index并压入栈顶,消耗栈顶的1个this. 栈底到栈顶:this、index_for_ladd 5: dup2_x1 // 复制栈顶index数值,并插入第1个this下面. 栈底到栈顶:index_for_return、this、index_for_ladd 6: lconst_1 // 将long类型常量1压入栈顶 7: ladd // 将栈顶的2个long类型数值相加,并将结果压入栈顶. 栈底到栈顶:index_for_lreturn、this、index_for_putfield 8: putfield #12 // Field index:J. 将栈顶数值赋值给实例字段index 11: lreturn Constant pool: #1 = Class #2 // jvm/specification/se8/chapter3/NextIndex #2 = Utf8 jvm/specification/se8/chapter3/NextIndex #3 = Class #4 // java/lang/Object #4 = Utf8 java/lang/Object #5 = Utf8 index #6 = Utf8 J #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Methodref #3.#11 // java/lang/Object."<init>":()V #11 = NameAndType #7:#8 // "<init>":()V #12 = Fieldref #1.#13 // jvm/specification/se8/chapter3/NextIndex.index:J #13 = NameAndType #5:#6 // index:J
dup2_x1指令
复制栈顶的1个或2个值,并将其插入到栈顶的2个或3个值下面。
在预备知识中,我们对局部变量的Slot做了简单说明。可以简单理解为,long类型和double类型占2个Slot,其他类型占1个Slot。
下面拆解一下指令的定义(借用局部变量的Slot概念来描述,有点不太严谨,但易于理解与记忆。)。
复制栈顶的1个或2个值 :
1个可以是long类型和double类型,2个是其他类型,共2个Slot。
插入到栈顶的2个或3个值下面 :
如果复制的是1个值(即栈顶是long或double,共2个Slot),那么插入到栈顶的2个值(栈顶1个long或double,下面1个其他类型,共3个Slot)下面。
如果复制的是2个值(即栈顶是2个其他类型数值,共2个Slot),那么插入到栈顶的3个值(栈顶3个都是其他类型,共3个Slot)下面。
简单理解,dup2_x1指令的作用就是将栈顶的2个Slot的值复制并插入到栈顶的3个Slot的值下面。
对于本实例,执行dup2_x1指令之前的栈结构为(栈底到栈顶):this、index。由于index为long类型,占2个Slot。this为引用类型,占1个Slot。因此,dup2_x1指令将栈顶的2个Slot的index值复制并插入到栈顶的3个Slot的this引用下面。
dup总共有6个指令,分别是dup、dup_x1、dup_x2、dup2、dup2_x1和dup2_x2。初看这些指令,容易混淆而难以理解。 经过分类和找规律,可以通过"指令系数法"来理解记忆,非常简单:
操作数栈管理指令共有9个,上面已经介绍了6个。剩下的3个用同样的方法就很容易理解了:
备注:指令系数法是自己为了方便记忆起的名字
参考
《The Java Virtual Machine Specification, Java SE 8 Edition》
《Java虚拟机规范》(Java SE 8版)
《深入理解Java虚拟机 JVM高级特性与最佳实践》
转载请注明来源:http://zhanjia.iteye.com/blog/2432142
二进制之路