当前位置: 首页 > news >正文

黑马JVM解析笔记(四):Javap图解指令流程,深入理解Java字节码执行机制

引言

在Java开发中,理解字节码指令的执行流程对于掌握Java虚拟机(JVM)的工作原理至关重要。本文将通过一个具体案例,结合javap反编译结果,详细图解Java字节码指令的执行过程,帮助读者深入理解JVM的运行时机制。

1. 案例Java代码

public class Demo3_1 {public static void main(String[] args) {int a = 10;int b = Short.MAX_VALUE + 1;  // 32767 + 1 = 32768int c = a + b;System.out.println(c);  // 输出32778}
}

2. Class文件反编译结果

使用javap -v命令反编译class文件,我们主要关注main方法的字节码:

public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=4, args_size=10: bipush        10   // 将10压入操作数栈2: istore_1           // 存储到局部变量1(a)3: ldc           #3   // 加载常量池#3项(32768)5: istore_2           // 存储到局部变量2(b)6: iload_1            // 加载局部变量1(a)7: iload_2            // 加载局部变量2(b)8: iadd               // 执行加法9: istore_3           // 结果存储到局部变量3(c)10: getstatic     #4   // 获取System.out13: iload_3            // 加载局部变量3(c)14: invokevirtual #5   // 调用println方法17: return             // 方法返回

关键信息解析

  • descriptor([Ljava/lang/String;)V 表示方法参数为String数组,返回类型为void
  • flagsACC_PUBLIC, ACC_STATIC 表示public static方法
  • stack=2, locals=4, args_size=1:操作数栈深度为2,局部变量表大小为4(含参数args),1个参数
  • 常量池#3 对应32768,#4 对应System.out字段,#5 对应println方法

编译期优化Short.MAX_VALUE + 1在编译时被计算为常量32768,直接存储在常量池中

3. 字节码执行流程详解

3.1 JVM运行时结构初始化

JVM运行时结构初始化

  • 方法区:加载类信息、常量池和字节码指令
  • :为main方法创建栈帧,包含局部变量表和操作数栈
  • 程序计数器:指向当前执行的指令地址

3.2 指令执行流程图解

1. bipush 10:将10压入操作数栈

bipush 10

  • 将整数值10推送到操作数栈顶
  • 类似指令:sipush(short值),ldc(int值)
2. istore_1:存储到局部变量1(a)

istore_1

  • 弹出栈顶值(10)并存入局部变量表slot 1
  • 局部变量表:slot 0=args, slot 1=10
3. ldc #3:加载常量池#3项(32768)

ldc #3

  • 从常量池加载#3项(32768)到操作数栈
  • 常量池在类加载时已初始化
4. istore_2:存储到局部变量2(b)

istore_2

  • 弹出栈顶值(32768)存入局部变量表slot 2
  • 局部变量表:slot 0=args, slot 1=10, slot 2=32768
5. iload_1iload_2:加载局部变量到操作数栈

iload指令

  • iload_1:加载slot 1的值(10)到操作数栈
  • iload_2:加载slot 2的值(32768)到操作数栈
  • 操作数栈状态:[10, 32768]
6. iadd:执行加法操作

iadd指令

  • 弹出栈顶两个元素(10和32768)
  • 执行加法运算:10 + 32768 = 32778
  • 结果压回操作数栈:[32778]
7. istore_3:结果存储到局部变量3©

istore_3

  • 弹出栈顶值(32778)存入局部变量表slot 3
  • 局部变量表完整状态:args, a=10, b=32768, c=32778
8. getstatic #4:获取System.out静态字段

getstatic #4

  • 解析常量池#4项(System.out字段引用)
  • 获取PrintStream对象引用压入操作数栈
9. iload_3:加载局部变量3©到操作数栈

iload_3

  • 加载slot 3的值(32778)到操作数栈
  • 操作数栈状态:[PrintStream@ref, 32778]
10. invokevirtual #5:调用println方法

invokevirtual #5

  1. 解析常量池#5项(println方法引用)
  2. 创建新的栈帧用于println方法
  3. 传递参数(32778)到新栈帧
  4. 执行println方法打印结果
11. return:方法返回
  • 弹出main方法栈帧
  • 程序执行结束

4. 关键指令深度解析

getstatic #4 指令详解

getstatic指令用于获取类的静态字段值,其执行过程如下:

  1. 解析字段引用

    • 根据常量池索引#4找到Fieldref常量
    • 解析出类名(java/lang/System)、字段名(out)和类型(Ljava/io/PrintStream;)
  2. 类加载检查

    • 如果System类未加载,触发类加载过程
    • 验证字段访问权限(public静态字段可直接访问)
  3. 获取字段值

    // 伪代码表示字段访问过程
    Class systemClass = ClassLoader.loadClass("java.lang.System");
    Field outField = systemClass.getField("out");
    Object printStream = outField.get(null); // 静态字段,实例参数为null
    
  4. 值压栈

    • 将获取的PrintStream引用压入操作数栈

性能提示:首次执行getstatic会有解析开销,后续执行直接使用缓存结果

方法调用过程

invokevirtual指令执行流程:

  1. 创建新的栈帧(包含局部变量表和操作数栈)
  2. 将调用者对象(System.out)和参数(c的值)压入新栈帧
  3. 转移控制权到新方法
  4. 方法执行完毕后,返回值压入调用者栈帧的操作数栈
  5. 销毁被调用方法的栈帧

5. 总结与思考

通过本案例的图解分析,我们可以得出以下结论:

  1. 编译期优化:常量表达式在编译阶段计算并存入常量池
  2. 栈式架构:JVM使用操作数栈作为计算中间结果的存储区
  3. 局部变量表:方法参数和局部变量存储在固定大小的表中
  4. 指令原子性:每个字节码指令完成一个基本操作
  5. 方法调用:涉及新栈帧的创建和上下文切换
http://www.lqws.cn/news/531523.html

相关文章:

  • 创建Django项目
  • JVM调优实战 Day 7:JVM线程分析与死锁排查
  • 动态库与静态库【Linux】
  • 前端替换打包后文件中的内容方案(可用于渗透测试后将问题版本号清空临时解决方案)
  • 事务相关问题
  • 数学:逆元,同余
  • 热点代码探测确定何时JIT
  • Codeforces Educational Round 180 题解
  • 【25软考网工】第十章 网络规划与设计(2)网络规划与分析、网络结构与功能
  • [按键手机安卓/IOS脚本插件开发] 按键插件调试与判断循环结构辅助工具
  • Jenkins执行Jenkinsfile报错
  • 可用的.gitignore文件
  • LDO的自放电功能
  • UE Universal Camera 相机插件-限制镜头在区域内移动
  • 认识Jacobian
  • 数学:初步了解什么是线性代数?
  • 开发语言漫谈-R语言
  • 超实用AI工具分享——ViiTor AI视频配音功能教程(附图文)
  • LeVERB:利用潜视觉-语言指令进行人形机器人全身控制
  • Oracle数据库捕获造成死锁的SQL语句
  • SDL渲染RGB图像
  • 高通手机跑AI系列之——穿衣试装算法
  • YOLOv10tensorRT推理代码C++
  • 虚拟化测试工具Parasoft Virtualize的两大核心功能及应用
  • 如何写出优秀的单元测试?
  • 【附源码】旅游网站系统设计与实现+SpringBoot + Vue (前后端分离)
  • 设计模式 | 建造者模式
  • 【机器学习深度学习】线性代数
  • 提升开发思维的设计模式(下)
  • Apache 支持 HTTPS