`

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

阅读更多

Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义的操作码以及跟随其后的零至多个代表此操作所需参数的操作数所构成。虚拟机中许多指令并不包含操作数,只有一个操作码。

Java虚拟机限制操作码的长度为1个字节,因此最多只能有256个指令。

指令格式

以下指令格式,是基于Oracle JDK编译后,通过javap工具生成的指令描述格式。

<index> <opcode> [<operand1> [<operand2>...]] [<comment>]

<index>

指令操作码在方法字节码指令数组中的索引,也可以认为是相对于方法起始处的字节偏移量。其中,指令数组指方法对应的Code属性的code[]数组,该数组用于存放方法的字节码指令。

该索引可以作为控制转移指令的跳转目标。例如,goto 8指令表示跳转到索引为8的指令上继续执行。

<opcode>

指令的操作码助记符。例如,iconst_0、istore_1、iload_1和return等。

<operandN>

指令操作数,一条指令可以有0至多个操作数。例如,iconst_0没有操作数,bipush有1个操作数,iinc有2个操作数。

<comment>

指令行尾的注释。注释内容通常以//开始。

每一行中,表示运行时常量池索引的操作数前,会有一个井号。在指令后的注释中,会带有对这个操作数的描述,例如:

 1: invokespecial #8    // Method java/lang/Object."<init>":()V  
10: ldc2_w        #19   // double 100.0d

实例分析

以下实例均使用JDK 1.8编译,并使用javap生成字节码指令清单。

代码1

void spin() {
    int i;
    for (i = 0; i < 100; i++) {
        ; // Loop body is empty
    }
}

字节码指令序列



 

iinc用于实现局部变量的自增操作。在所有字节码指令中,只有该指令可直接用于操作局部变量。

对于非-1至5的int类型常量(对应指令iconst_N),使用bipush来将单字节常量值推至栈顶。

JVM对int类型提供了比较和跳转相结合的if指令,例如该例子中的if_icmplt指令。而对于long、float和double,则需要先通过各自的cmp比较指令计算出int类型结果,再结合int类型的if指令判断后再进行跳转。

代码2

void dspin() {
    double i;
    for (i = 0.0; i < 100.0; i++) {
        ; // Loop body is empty
    }
}

字节码指令序列



 

其中,double类型占用局部变量的2个Slot,局部变量索引号从0开始,因此dstore_1对应的局部变量索引为1和2。

由于iinc只针对int类型进行自增操作,JVM并没有提供相应的指令来操作double类型。因此,需要借助dadd来实现double类型的自增操作。

同样,以if开头的比较跳转指令,都只用于int类型。但JVM另外提供了dcmpg、dcmpl来比较两个double类型数值的大小,然后将比较结果(1,0,-1)压入栈顶。最后,再使用int类型的if判断指令来进行判断跳转。

dcmpg与dcmpl的区别仅在于,当比较的其中一个值为NaN时,dcmpg将1压入栈顶,而dcmpl将-1压入栈顶。

ldc相关指令都是将常量值从常量池中推至栈顶。

代码3

void sspin() {
    short i;
    for (i = 0; i < 100; i++) {
        ; // Loop body is empty
    }
}

字节码指令序列



 

short类型同样需要通过多条指令来实现i++操作,对应于索引号为5至9的指令。首先,使用iadd实现2个int类型数值相加,再使用i2s指令将int类型结果强制转换为short类型,最后使用istore_1指令将结果存回局部变量i。

对于byte、char和short类型数据,JVM并未提供像int类型一样丰富的直接操作指令。然而,由于byte、char和short类型数据都可以自动宽化转换为int类型,因此均可通过int类型的指令来操作。唯一额外的代价是要将操作结果截短至它们的有效范围内


参考

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

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

 

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

 

个人公众号

二进制之路

 

  • 大小: 12.1 KB
  • 大小: 18.4 KB
  • 大小: 11.6 KB
0
0
分享到:
评论

相关推荐

    最新java面试专题01-JVM

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

    JAVA 范例大全 光盘 资源

    实例4 变量和常量 9 实例5 基本数据类型转换 10 实例6 操作多种运算符 12 实例7 不同数制间的转换 17 实例8 多种方式实现阶乘的算法 20 第3章 流程控制语句 23 实例9 打印任一年日历 23 实例10 控制台输出...

    Java常量池理解与总结

     final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。  Class文件中的常量池  在Class文件结构中,头的4个字节用于存储魔数Magic Number,用于确定一个文件是否能被JVM接受,再...

    java范例开发大全

    实例11 常量与变量 18 实例12 各种进制的转换 19 实例13 Java中的进制与移位运算符 22 第3章 条件控制语句(教学视频:75分钟) 26 3.1 if控制语句 26 实例14 判断输入的年份是否为闰年 26 实例15 抽奖活动 27 3.2 ...

    Java范例开发大全 (源程序)

     实例11 常量与变量 18  实例12 各种进制的转换 19  实例13 Java中的进制与移位运算符 22  第3章 条件控制语句(教学视频:75分钟) 26  3.1 if控制语句 26  实例14 判断输入的年份是否为闰年 26  实例...

    java范例开发大全(pdf&源码)

    实例11 常量与变量 18 实例12 各种进制的转换 19 实例13 Java中的进制与移位运算符 22 第3章 条件控制语句(教学视频:75分钟) 26 3.1 if控制语句 26 实例14 判断输入的年份是否为闰年 26 实例15 抽奖活动 27 3.2 ...

    java范例开发大全源代码

     实例23 利用for循环输出几何图形 36  实例24 杨辉三角 38  3.3 while语句 39  实例25 求1到100之间的和 39  实例26 存上100元需要多少天 40  实例27 输出100之间的所有偶数 41  实例28 如何判断...

    Java范例开发大全(全书源程序)

    实例11 常量与变量 18 实例12 各种进制的转换 19 实例13 Java中的进制与移位运算符 22 第3章 条件控制语句(教学视频:75分钟) 26 3.1 if控制语句 26 实例14 判断输入的年份是否为闰年 26 实例15 抽奖活动 ...

    Java常见面试问题整理.docx

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

    深入Java虚拟机(原书第2版).pdf【附光盘内容】

    10.4 弹出栈顶部元素,将其赋给局部变量 10.5 wide指令 10.6 一个模拟:“fibonacci forever” 10.7 随书光盘 10.8 资源页 第11章 类型转换 11.1 转换操作码 11.2 一个模拟:“conversion diversion”...

    Java内存分配分析/栈内存、堆内存

     保存局部变量的值包括  1、基本数据变量  2、引用变量的保存,即堆对象的引用。当然也可以用来保存加载方法时的帧。  · 寄存器  JVM内部虚拟寄存器,存取速度非常快,程序不可控制。  · 常量池  ...

    java编程基础,应用与实例

    5.4 局部变量和成员变量 67 5.5 this引用 68 5.6 静态变量与静态方法 70 5.7 成员与静态方法的关系 71 5.8 包与导入 72 5.9 访问控制符 74 5.10 重载 77 5.11 构造函数 79 5.12 类的初始化 83 ...

    Android面试(一)Java虚拟机内存结构分析

    虚拟机栈:线程创建之初,Java虚拟机会为每一个线程开辟一块虚拟机栈空间,存储线程方法调用的局部变量,计算中间量,参数等,是线程私有的内存区域。 本地方法栈:线程私有的用于native方法引用的内存栈空间。 程序...

    【05-面向对象(下)】

    •对一个final变量来说,不管它是类变量、实例变量,还是局部变量,只要该变量满足3个条件,这个final变量就 不再是一个变量,而是相当于一个直接量。  –使用final修饰符修饰;  –在定义该final变量时指定...

    【JVM和性能优化】4. 编写高效优雅Java代码常用方法

    局部变量作用域最小化14. 对于精度技术不用float或double15.字符串操作少用String16.对资源的close建议分开操作17. 数据类型转换18. 不用的对象记得置NULL19. if判断常量在前20. 字符串变量比较的时候21. 同步方法...

    Android中的内存泄露

    可以作为GCROOT节点的对象虚拟机栈的栈帧的局部变量表引用的对象本地方法栈JNI引用的对象方法区的静态变量和常量所引用的对象引用计数法内存图1.step1:GcObject实例1的引用计数+1,目前为12.step2:Gc

    java 面试题 总结

    然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类为。取而代之,在子类中...

    易语言程序免安装版下载

    修改BUG:超级列表框在属性“整行选择”为真时,鼠标单击第一列右面也会导致第一列中的选择框被选中或取消选中。 21. 修改BUG:Sqlite3数据库支持库中“Sqlite数据库.取错误文本()”返回的文本是UTF-8编码(应是GB...

Global site tag (gtag.js) - Google Analytics