深入拆解 Java 虚拟机
郑雨迪
Oracle 高级研究员,计算机博士
87446 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 40 讲
模块四:黑科技 (3讲)
深入拆解 Java 虚拟机
15
15
1.0x
00:00/00:00
登录|注册

19 | Java字节码(基础篇)

ret
jsr
返回指令
lookupswitch
tableswitch
条件跳转指令
goto
arraylength
multianewarray
anewarray
newarray
invokedynamic
invokeinterface
invokevirtual
invokespecial
invokestatic
putfield
getfield
putstatic
getstatic
monitorexit
monitorenter
athrow
checkcast
instanceof
new
iinc
astore
aload
istore
iload
swap
pop
dup
ldc
sipush
bipush
iconst
计算相关指令
控制流指令
数组相关指令
方法调用指令
字段访问指令
Java相关指令
局部变量区访问指令
操作数栈专用指令
加载常量指令
Java字节码知识关系脑图

该思维导图由 AI 生成,仅供参考

在前面的篇章中,有不少同学反馈对 Java 字节码并不是特别熟悉。那么今天我便来系统性地介绍一遍 Java 字节码。

操作数栈

我们知道,Java 字节码是 Java 虚拟机所使用的指令集。因此,它与 Java 虚拟机基于栈的计算模型是密不可分的。
在解释执行过程中,每当为 Java 方法分配栈桢时,Java 虚拟机往往需要开辟一块额外的空间作为操作数栈,来存放计算的操作数以及返回结果。
具体来说便是:执行每一条指令之前,Java 虚拟机要求该指令的操作数已被压入操作数栈中。在执行指令时,Java 虚拟机会将该指令所需的操作数弹出,并且将指令的结果重新压入栈中。
以加法指令 iadd 为例。假设在执行该指令前,栈顶的两个元素分别为 int 值 1 和 int 值 2,那么 iadd 指令将弹出这两个 int,并将求得的和 int 值 3 压入栈中。
由于 iadd 指令只消耗栈顶的两个元素,因此,对于离栈顶距离为 2 的元素,即图中的问号,iadd 指令并不关心它是否存在,更加不会对其进行修改。
Java 字节码中有好几条指令是直接作用在操作数栈上的。最为常见的便是 dup: 复制栈顶元素,以及 pop:舍弃栈顶元素。
dup 指令常用于复制 new 指令所生成的未经初始化的引用。例如在下面这段代码的 foo 方法中,当执行 new 指令时,Java 虚拟机将指向一块已分配的、未初始化的内存的引用压入操作数栈中。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了Java字节码的基本概念和操作,包括操作数栈、局部变量区的作用以及常见指令的使用方法。文章详实地讲解了Java字节码的各种类型,如加载常量指令、操作数栈专用指令、局部变量区访问指令、Java相关指令、方法调用指令、数组相关指令、控制流指令和计算相关指令。通过示例代码和对应的字节码,读者可以更直观地理解Java字节码的运行机制。总体而言,本文适合对Java字节码感兴趣的读者阅读。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《深入拆解 Java 虚拟机》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(22)

  • 最新
  • 精选
  • 1:.Java代码由Java的语言语法组成,有开发人员来编写 2:.class 代码有Java编译器来编译,Java编译器也是有对应的开发人员来编写的,.class代码有字节码指令来组成,如果人理解Java字节码指令集比较简单也可以直接编写.class代码 3:Java对应的机器码有JVM来编译出来,原料是.class代码,如果人类理解机器码比较容易,那么可能变成就直接在机器硬件上直接编写机器码了 4:高级语言的出现是为提高人编写代码的效率,我们学习.class字节码指令集、JVM、机器码等的知识,是为了使我们编写高级语言代码能更好的在机器硬件上的执行效率更高,从高级语言的代码到能在机器上运行的机器码,中间经过了好几层的转换,所以,了解每一层是怎么转换就能更快的定位出高级语言代码的性能瓶颈了,感觉是为了在人的编码效率和机器的执行效率之间找平衡点 有个疑问❓ 没太理解,JVM基于栈的计算模型的原因,推测可能是为了更简单的实现和更高的性能但是是怎么做到的呢?请老师解释一下

    作者回复: 基于栈的计算模型确实是为了实现起来容易一些,但它并不高效,因为没有使用底层体系架构的寄存器。 在JVM中,只有解释器完整地模拟出该计算模型。即时编译器在解析字节码时会使用一个虚拟的栈计算模型,但是在接下来的编译优化,以及生成的机器码就不用了。

    2018-09-07
    2
    23
  • 李二木
    为什么局部变量要初始化?想请老师专业解答下!

    作者回复: 如果是虚拟机初始化局部变量,那么它需要掌握每个变量的生命周期,以便初始化共享同一下标的局部变量。实现起来比较费事。 另一方面,从代码规范来看,使用未经初始化的局部变量意义不明。

    2018-09-03
    3
    12
  • 熊猫酒仙
    C/C++的汇编指令,会有大量寄存器的操作 请问java的指令会用到寄存器吗?

    作者回复: Java字节码不会,但是底下的实现(比如解释执行器,即时编译器)是会的。

    2018-09-07
    9
  • Shine
    “因此,我们需要利用 dup 指令复制一份 new 指令的结果,并用来调用构造器。当调用返回之后,操作数栈上仍有原本由 new 指令生成的引用去...” 第一步栈顶压入new对象的引用r0,执行dup后复制r0得到r1,压入栈顶。r1用于调用构造器,完成后会pop, 留下栈顶元素r0。不知我这样理解对不? 我的问题是为什么要dup呢?直接用r0不做pop不好吗?

    作者回复: 构造器是没有返回结果的,所以不用pop。如果不dup的话,就只有一个r0,在调用构造器时用掉了,程序就再没有对该新建对象的引用了。

    2018-09-06
    3
    7
  • 对方正在输入
    在JVM中,每个方法中,代码语句执行完毕,是不是都会默认有个return

    作者回复: 正常执行路径会有return,有返回值的是ireturn,areturn这些。异常执行路径会有athrow。你可以试试查看只有一句throw new Exception()的方法

    2018-11-12
    5
  • Void_seT
    数组访问指令表,int文稿中写的iaload,iastore;表格中列的iastore和istore

    作者回复: 多谢指出!

    2018-09-03
    1
  • 曲东方
    详尽,赞👍 随便找几断代码,javap反编译,查jvm手册一会儿就明白了
    2018-09-03
    37
  • Jerry银银
    老师,请教下,在专栏中(关于虚拟机的书籍中)有提到:Java虚拟机大部分都是基于栈,有些虚拟机是基于寄存器的,比如Android的Dalvik和ART。 这听起来挺抽象的,老师能具体讲讲它们的区别? 是字节码执行的时候有区别的吗? 还是说字节码本身就有区别?
    2019-12-28
    3
  • 饭粒
    图文并茂,总结详尽!感觉这篇放在前面可能更好。
    2019-12-23
    3
  • 啸疯
    看的真爽,了解了很多字节码层面的细节,例如常量相加后赋值给变量,那么在字节码层面其实直接就是相加后的值,再比如两个string的相加,字节码层面其实也是调用stringbuiler不断append后tostring来实现的
    2021-12-28
    2
收起评论
显示
设置
留言
22
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部