`

JVM指令分析实例五(操作数栈)

阅读更多

本篇为《JVM指令分析实例》的第五篇,相关实例均使用Oracle JDK 1.8编译,并使用javap生成字节码指令清单。

前几篇传送门:

JVM指令分析实例一(常量、局部变量、for循环)

JVM指令分析实例二(算术运算、常量池、控制结构)

JVM指令分析实例三(方法调用、类实例)

JVM指令分析实例四(数组、switch)

预备知识

局部变量表的变量槽(Variable Slot)

局部变量表的容量以变量槽(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。初看这些指令,容易混淆而难以理解。经过分类和找规律,可以通过"指令系数法"来理解记忆,非常简单:

  • 不带_x的指令是复制栈顶数据并压入栈顶。包括两个指令,dup和dup2
  • 带_x的指令是复制栈顶数据并插入栈顶以下的某个位置。共有4个指令
  • dup的系数代表要复制的Slot个数
    • dup开头的指令用于复制1个Slot的数据。例如1个int或1个reference类型数据
    • dup2开头的指令用于复制2个Slot的数据。例如1个long,或2个int,或1个int+1个float类型数据
  • 对于带_x的复制插入指令,只要将指令的dup和x的系数相加,结果即为需要插入的位置。因此
    • dup_x1插入位置:1+1=2,即栈顶2个Slot下面。
    • dup_x2插入位置:1+2=3,即栈顶3个Slot下面。
    • dup2_x1插入位置:2+1=3,即栈顶3个Slot下面。
    • dup2_x2插入位置:2+2=4,即栈顶4个Slot下面。

 

操作数栈管理指令共有9个,上面已经介绍了6个。剩下的3个用同样的方法就很容易理解了:

  • pop:将栈顶的1个Slot数值出栈。例如1个short类型数值
  • pop2:将栈顶的2个Slot数值出栈。例如1个double类型数值,或者2个int类型数值
  • swap:交换栈顶的2个Slot数值位置。Java虚拟机没有提供交换两个64位数据类型(long、double)数值的指令。

备注:指令系数法是自己为了方便记忆起的名字

 

参考

《The Java Virtual Machine Specification, Java SE 8 Edition》

《Java虚拟机规范》(Java SE 8版)

《深入理解Java虚拟机 JVM高级特性与最佳实践》

 


 

转载请注明来源:http://zhanjia.iteye.com/blog/2432142

 

个人公众号

二进制之路

 

0
0
分享到:
评论

相关推荐

    最新java面试专题01-JVM

    栈是线程私有的内存区域,每个方法执行时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接等信息;程序计数器是线程私有的,用于记录当前虚拟机正在执行的线程指令。 JVM生命周期:JVM的生命周期始于启动...

    Java常见面试问题整理.docx

    2.Java虚拟机栈:描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧 ,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在...

    java 面试题 总结

    ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector...

    超级有影响力霸气的Java面试题大全文档

     ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,...

    net学习笔记及其他代码应用

    答:Class可以被实例化,属于引用类型,是分配在内存的堆上的,Struct属于值类型,是分配在内存的栈上的. [Page] 26.根据委托(delegate)的知识,请完成以下用户控件中代码片段的填写: namespace test { public ...

    java面试题

    72.9. action是单实例还是多实例,为什么? 73 72.10. Struts的validate框架是如何验证的? 74 72.11. dispatchAction是用什么技术实现的? 74 72.12. struts2.0的mvc模式?与struts1.0的区别? 74 72.13. struts1.2...

    oracle 11g安装配置

    增大用户oracle的进程数、文件数限制 [root@dbserver ~]# vi /etc/sysctl.conf …… fs.aio-max-nr = 1048576 限制并发未完成的请求,应该设置避免I/O子系统故障 fs.file-max = 6815744 文件句柄设置代表linux系统中...

    千方百计笔试题大全

    10、简述逻辑操作(&,|,^)与条件操作(&&,||)的区别? 9 11、heap 和stack 有什么区别? 9 12、Math.round(11.5) 等于多少? Math.round(-11.5)等于多少? 9 13、swtich 是否能作用在byte 上,是否能作用在long 上,是否...

    java面试宝典

    10、简述逻辑操作(&,|,^)与条件操作(&&,||)的区别? 9 11、heap 和stack 有什么区别? 9 12、Math.round(11.5) 等于多少? Math.round(-11.5)等于多少? 9 13、swtich 是否能作用在byte 上,是否能作用在long 上,是否...

    java基础题 很全面

    42. 描述一下JVM加载class文件的原理机制? 12 43. char型变量中能不能存贮一个中文汉字?为什么? 12 44. 多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么? 12 45. 线程的基本概念、线程的基本状态以及...

    Java面试宝典2010版

    五. 数据库部分 1、用两种方式根据部门号从高到低,工资从低到高列出每个员工的信息。 2、列出各个部门中工资高于本部门的平均工资的员工数和部门号,并按部门号排序 3、存储过程与触发器必须讲,经常被面试到? ...

    最新Java面试宝典pdf版

    五. 数据库部分 91 1、用两种方式根据部门号从高到低,工资从低到高列出每个员工的信息。 91 2、列出各个部门中工资高于本部门的平均工资的员工数和部门号,并按部门号排序 91 3、存储过程与触发器必须讲,经常被...

    Java面试笔试资料大全

    五. 数据库部分 91 1、用两种方式根据部门号从高到低,工资从低到高列出每个员工的信息。 91 2、列出各个部门中工资高于本部门的平均工资的员工数和部门号,并按部门号排序 91 3、存储过程与触发器必须讲,经常被...

    Java面试宝典-经典

    五. 数据库部分 91 1、用两种方式根据部门号从高到低,工资从低到高列出每个员工的信息。 91 2、列出各个部门中工资高于本部门的平均工资的员工数和部门号,并按部门号排序 91 3、存储过程与触发器必须讲,经常被...

    JAVA面试宝典2010

    五. 数据库部分 91 1、用两种方式根据部门号从高到低,工资从低到高列出每个员工的信息。 91 2、列出各个部门中工资高于本部门的平均工资的员工数和部门号,并按部门号排序 91 3、存储过程与触发器必须讲,经常被...

    java面试题大全(2012版)

    五. 数据库部分 91 1、用两种方式根据部门号从高到低,工资从低到高列出每个员工的信息。 91 2、列出各个部门中工资高于本部门的平均工资的员工数和部门号,并按部门号排序 91 3、存储过程与触发器必须讲,经常被...

    Java面试宝典2012版

    五. 数据库部分 91 1、用两种方式根据部门号从高到低,工资从低到高列出每个员工的信息。 91 2、列出各个部门中工资高于本部门的平均工资的员工数和部门号,并按部门号排序 91 3、存储过程与触发器必须讲,经常被...

    java面试宝典2012

    五. 数据库部分 99 1、用两种方式根据部门号从高到低,工资从低到高列出每个员工的信息。 99 2、列出各个部门中工资高于本部门的平均工资的员工数和部门号,并按部门号排序 100 3、存储过程与触发器必须讲,经常被...

Global site tag (gtag.js) - Google Analytics