深入拆解Java虚拟机
郑雨迪
Oracle 高级研究员,计算机博士
立即订阅
28026 人已学习
课程目录
已完结 39 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 为什么我们要学习Java虚拟机?
免费
模块一:Java虚拟机基本原理 (12讲)
01 | Java代码是怎么运行的?
02 | Java的基本类型
03 | Java虚拟机是如何加载Java类的?
04 | JVM是如何执行方法调用的?(上)
05 | JVM是如何执行方法调用的?(下)
06 | JVM是如何处理异常的?
07 | JVM是如何实现反射的?
08 | JVM是怎么实现invokedynamic的?(上)
09 | JVM是怎么实现invokedynamic的?(下)
10 | Java对象的内存布局
11 | 垃圾回收(上)
12 | 垃圾回收(下)
模块二:高效编译 (12讲)
【工具篇】 常用工具介绍
13 | Java内存模型
14 | Java虚拟机是怎么实现synchronized的?
15 | Java语法糖与Java编译器
16 | 即时编译(上)
17 | 即时编译(下)
18 | 即时编译器的中间表达形式
19 | Java字节码(基础篇)
20 | 方法内联(上)
21 | 方法内联(下)
22 | HotSpot虚拟机的intrinsic
23 | 逃逸分析
模块三:代码优化 (10讲)
24 | 字段访问相关优化
25 | 循环优化
26 | 向量化
27 | 注解处理器
28 | 基准测试框架JMH(上)
29 | 基准测试框架JMH(下)
30 | Java虚拟机的监控及诊断工具(命令行篇)
31 | Java虚拟机的监控及诊断工具(GUI篇)
32 | JNI的运行机制
33 | Java Agent与字节码注入
模块四:黑科技 (3讲)
34 | Graal:用Java编译Java
35 | Truffle:语言实现框架
36 | SubstrateVM:AOT编译框架
尾声 (1讲)
尾声 | 道阻且长,努力加餐
深入拆解Java虚拟机
登录|注册

24 | 字段访问相关优化

郑雨迪 2018-09-14
在上一篇文章中,我介绍了逃逸分析,也介绍了基于逃逸分析的优化方式锁消除、栈上分配以及标量替换等内容。
其中的标量替换,可以看成将对象本身拆散为一个个字段,并把原本对对象字段的访问,替换为对一个个局部变量的访问。
class Foo {
int a = 0;
}
static int bar(int x) {
Foo foo = new Foo();
foo.a = x;
return foo.a;
}
举个例子,上面这段代码中的bar方法,经过逃逸分析以及标量替换后,其优化结果如下所示。(确切地说,是指所生成的 IR 图与下述代码所生成的 IR 图类似。之后不再重复解释。)
static int bar(int x) {
int a = x;
return a;
}
由于 Sea-of-Nodes IR 的特性,局部变量不复存在,取而代之的是一个个值。在例子对应的 IR 图中,返回节点将直接返回所输入的参数。
经过标量替换的bar方法
下面我列举了bar方法经由 C2 即时编译生成的机器码(这里略去了指令地址的前 48 位)。
# {method} 'bar' '(I)I' in 'FieldAccessTest'
# parm0: rsi = int // 参数x
# [sp+0x20] (sp of caller)
0x06a0: sub rsp,0x18 // 创建方法栈桢
0x06a7: mov QWORD PTR [rsp+0x10],rbp // 无关指令
0x06ac: mov eax,esi // 将参数x存入返回值eax中
0x06ae: add rsp,0x10 // 弹出方法栈桢
0x06b2: pop rbp // 无关指令
0x06b3: mov r10,QWORD PTR [r15+0x70] // 安全点测试
0x06b7: test DWORD PTR [r10],eax // 安全点测试
0x06ba: ret
在 X86_64 的机器码中,每当使用 call 指令进入目标方法的方法体中时,我们需要在栈上为当前方法分配一块内存作为其栈桢。而在退出该方法时,我们需要弹出当前方法所使用的栈桢。
由于寄存器 rsp 维护着当前线程的栈顶指针,因此这些操作都是通过增减寄存器 rsp 来实现的,即上面这段机器码中偏移量为 0x06a0 以及 0x06ae 的指令。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《深入拆解Java虚拟机》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(15)

  • 誓言的梦
    有除零的异常 是通过什么手段/机制知道 还是写死的除法不能优化 或者回不回是通过 检测当输入除数为0就不优化 输入不为0时才优化
    2018-12-19
    3
  • qpm
    老师,请问一下,读这个专栏有点像涨视野的感觉,暂时来说对我看代码提供了新的灵感,但目前只能听,没法提问,这种感觉是是因为我底子不够么?还是我实践得比较少?

    作者回复: 按照自己的节奏学习就好啦,等以后遇到问题了也可以回来提问的。

    2018-09-20
    2
  • 永烁星光
    希望老师推荐一本 JVM 书籍,(除了《深入理解java 虚拟机》,因为我发现最新出版也是2013年的)
    2018-09-17
    1
  • 三木子
    除法操作直接消除,返回return x+y; ?

    作者回复: 消除不了,有位同学答出来了,有除零异常

    2018-09-14
    1
  • Scott
    "其中真正的安全点测试是 0x06ba 指令"应该是0x06b7指令

    作者回复: 多谢指出!已修改!

    2018-09-14
    1
  • 随心而至
    JVM, 将字节码转成机器码,想深入了解挺难的,不过掌握其中的核心部分应该就够用了,毕竟咱不是JVM工程师,各有分工。
    2019-10-29
  • Joker
    越走到后面就是天书啊,一篇文章至少三遍
    2019-08-16
  • colin
    越了解,发现自己懂得越少
    2019-05-29
  • xingoo
    字段缓存
    存储优化
    死代码消除:冗余代码,不可达代码
    2019-05-20
  • 『LHCY』
    不知道现在发还会不会回复。
    while(a.flag){
        System.out.println();
    }
    这种方式,在flag没有被volatile多线程方式修改flag=false会退出循环,是因为打印函数中的锁阻止了字段优化吗
    2019-01-14
  • o
    大佬,可否专门开一章讲讲对象的属性会在那些时候触发更新主存/用户内存?分别是单线程不同方法,多线程不同方法、多线程相同方法。是否和方法体大小也有关系?例如就是在该篇文章中,如果while的代码块足够简单就会形成死循环,但是如果加了输出语句就不会(之前测试volatile关键字的时候遇到过,晚点贴一下主要代码🙏)
    2018-10-05
  • 永烁星光
    Return x+y ;

    作者回复: 课后实践中的除法无法优化,因为可能存在除零异常。即时编译器需要判断除数是否为0

    2018-09-14
  • Void_seT
    因为x/y会有除0异常,这部分代码是否会被优化掉,不太确定,望老师指点。

    作者回复: 对的,因为有除0异常所以编译器没法优化掉这个除法

    2018-09-14
  • Scott
    这一篇洋洋洒洒,其实覆盖了更多后端优化的算法,是否可以罗列一下对应算法名称供参考?

    作者回复: 我记得就叫read elimination和write elimination。第三节的是dead store elimination和partial redundancy elimination。

    Graal的相关代码在PEReadEliminationClosure以及ReadEliminationClosure中。可以自行阅读。

    2018-09-14
  • 三木子
    感觉写代码的能力水平跟编译器优化工作量还是有点关系的。

    作者回复: 一般我们写代码都会在保持代码可读性的同时,尽量减少编译器工作量

    2018-09-14
收起评论
15
返回
顶部