`

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
分享到:
评论

相关推荐

    JVM指令手册_jvm指令手册_

    理解JVM指令手册对于优化Java代码、进行内存分析、理解垃圾收集机制、排查运行时错误等方面都有重要作用。通过深入学习JVM指令,开发者可以更好地理解字节码层面的运行机制,从而编写出更高效、更稳定的Java程序。...

    JVM指令查询手册.pdf

    JVM指令分为五类:操作数栈管理指令、局部变量表操作指令、控制流指令、字节码操作指令和对象及数组操作指令。这些指令共同构成了Java程序的运行基础。 2. **操作数栈管理指令** 操作数栈是JVM中存储数据的地方。...

    JVM指令码表.zip

    1. **数据加载与存储指令**:如`iconst_m1`到`iconst_5`用于将-1至5的整数值直接压入操作数栈,`ldc`用于加载常量池中的基本类型或字符串,`aload`和`astore`系列用于对象引用的加载和存储。 2. **算术运算指令**:...

    JVM指令集 PDF 下载

    1. **加载和存储指令**:这类指令用于在局部变量表中加载和存储数据,例如`iload`用于从局部变量表加载一个整型值到操作数栈,`istore`则将操作数栈顶部的整型值存储回局部变量表。 2. **算术运算指令**:JVM提供了...

    JVM指令

    JVM指令由一个单字节的操作码(opcode)和可选的操作数组成。操作码定义了指令的行为,而操作数提供了指令执行所需的额外信息,如变量索引、常量池索引等。 ### 3. 字节码分析工具 为了便于查看和理解JVM指令,...

    JVM图解-JVM指令-JVM原型图.rar

    - 指令手册会列出所有这些指令,比如`iconst_5`表示将整数5压入操作数栈,`aload_0`用于将局部变量表的第一个元素(通常为this引用)加载到操作数栈。 3. **垃圾收集**: - JVM负责自动管理内存,通过垃圾收集...

    JVM指令手册(Java)

    例如,`iconst_1`是加载整数常量1到操作数栈的指令,它的指令码可能就是特定的8位数字。 2. 类型系统与操作数栈: JVM使用一种静态类型系统,这意味着在编译时类型就已经确定。在执行过程中,数据通过操作数栈进行...

    Java JVM程序指令码实例解析

    使用javap命令可以生成JVM指令码的详细信息,包括指令码的 opcode、操作数、栈帧信息等。 通过上面的示例,我们可以了解Java JVM程序指令码实例解析的过程,包括编译Java源代码、生成.class文件和反编译.class文件...

    00-JVM指令手册.zip

    了解JVM指令集有助于我们分析和优化Java代码,尤其是对于性能敏感的应用,理解JVM内部的运作机制可以更好地进行问题排查和性能调优。通过阅读"00-JVM指令手册.pdf",开发者可以深入理解这些概念,并应用到实际编程中...

    Java-JVM指令手册(2022最新版,中文全解析)

    每条JVM指令都会对操作数栈进行操作,如入栈、出栈、做算术运算等。 7. **指令集**:JVM有一套完整的指令集,包括加载/存储指令、算术逻辑指令、控制流指令、对象处理指令、类型检查和转换指令等。例如: - **a...

    JVM指令查询手册.rar

    此外,JVM指令查询手册通常会包含详细的指令格式、操作数解释以及每条指令的执行效果,便于开发者查阅和学习。通过深入研究JVM指令,开发者可以更精确地理解Java程序在JVM中的运行过程,这对于解决性能问题、设计...

    JVM指令手册.pdf

    这一系列指令主要用于将基本类型的常量推送到操作数栈的顶部。例如: - `iconst_m1` 到 `iconst_5` 分别用于将-1到5的整数值推送到栈顶。 - `lconst_0` 和 `lconst_1` 用于推送long型的0和1。 - `fconst_0` 到 `...

    JVM指令集(1).docx

    - `iload`, `lload`, `fload`, `dload` 和 `aload`:分别用于装载int、long、float、double和引用类型的值到操作数栈。 - `iload_0`至`iload_3`,`lload_0`至`lload_3`,`fload_0`至`fload_3`,`dload_0`至`dload_...

    jvm 详细介绍,了解jvm各个组成部分和功能

    JVM 的指令集采用了固定长度的格式,每条指令都是由一个操作码(Opcode)和零个或多个操作数组成。这种设计使得指令集既简单又易于扩展。 例如,“iload”表示加载整数到操作数栈,“anewarray”用于创建数组对象,...

    jdk,jvm源码

    堆存储对象实例,方法区存放类信息,程序计数器记录当前线程执行的指令地址,虚拟机栈保存每个方法的局部变量、操作数栈等,本地方法栈为JNI调用的本地方法服务。 3. 指令集:JVM使用一套基于栈的指令集,这些指令...

    java模拟jvm

    JVM执行的是字节码指令集,如`aload`(加载引用到操作数栈)、`iload`(加载整数到操作数栈)、`invokevirtual`(调用虚方法)等。模拟JVM需要理解并实现这些指令的解析和执行逻辑: 1. **解析字节码**:读取.class...

    java之jvm学习笔记十二(访问控制器的栈校验机制)

    2. 数量检查:操作数栈中的数据数量需与指令相对应。比如,二元运算指令需要栈顶有两元素,多出或不足都会触发异常。 3. 运算符限制:对于算术运算、逻辑运算等,JVM会确保操作符的操作数类型正确,避免出现如对...

    java虚拟机运行时数据区分析

    按照JVM规范,操作指令需要的参数是从操作数栈获得的(the operand stack)。 在JVM中,操作码可以分为不同的类别,例如: * Constants:将常量池的值或者已知的值压入操作数栈。 * Loads:将局部变量值压入操作数...

    jvm8虚拟机规范

    JVM8的字节码指令集包括操作数栈操作、局部变量操作、控制流、方法调用、数据类型转换等多种指令,它们构成了Java程序执行的基础。 5. 运行时数据区优化: JVM8引入了G1垃圾收集器、String去重复、堆内存并行压缩...

    深入理解JVM-java虚拟机栈.docx

    每当执行一条字节码指令,操作数栈可能会进行压栈或弹栈操作。 3. 动态连接(Dynamic Linking):每个栈帧都包含对所属方法的引用,以便在运行时进行动态连接。这使得Java方法调用能够灵活地查找和使用运行时常量池...

Global site tag (gtag.js) - Google Analytics