点击上方“方志朋”,选择“置顶或者星标”

你的关注意义重大!

来自:唐尤华

https://shipilev.net/jvm-anatomy-park/1-lock-coarsening-for-loops/

1. 写在前面

“JVM 解剖公园”是一个持续更新的系列迷你博客,阅读每篇文章一般需要5到10分钟。限于篇幅,仅对某个主题按照问题、测试、基准程序、观察结果深入讲解。因此,这里的数据和讨论可以当轶事看,并没有做一致性、写作风格、句法和语义错误、重复或一致性检查。如果选择采信文中内容,风险自负。

Aleksey Shipilёv,JVM 性能极客
推特 @shipilev
问题、评论、建议发送到 aleksey@shipilev.net

译注:锁粗化(Lock Coarsening)。锁粗化是合并使用相同锁对象的相邻同步块的过程。如果编译器不能使用锁省略(Lock Elision)消除锁,那么可以使用锁粗化来减少开销。

2. 问题

众所周知,Hotspot 确实进行了锁粗化优化,可以有效合并几个相邻同步块,从而降低锁开销。能够把下面的代码

synchronized (obj) {// 语句 1
}
synchronized (obj) {// 语句 2
}

转化为

synchronized (obj) {// 语句 1// 语句 2
}

问题来了,Hotspot 能否对循环进行这种优化?例如,把

for (...) {synchronized (obj) {// 一些操作}
}

优化成下面这样?

synchronized (this) {for (...) {// 一些操作}
}

理论上,没有什么能阻止我们这样做,甚至可以把这种优化看作只针对锁的优化,像 loop unswitching 一样。然而,缺点是可能把锁优化后变得过粗,线程在执行循环时会占据所有的锁。

译注:Loop unswitching 是一种编译器优化技术。通过复制循环主体,在 if 和 else 语句中放一份循环体代码,实现将条件句的内部循环移到循环外部,进而提高循环的并行性。由于处理器可以快速运算矢量,因此执行速度得到提升。

3. 实验

要回答这个问题,最简单的办法就是找到 Hotspot 优化的证据。幸运的是,有了 JMH 帮助这项工作变得非常简单。JMH 不仅在构建基准测试时有用,并且在分析基准测试方面同样好用。让我们从一个简单的基准测试开始:

@Fork(..., jvmArgsPrepend = {"-XX:-UseBiasedLocking"})
@State(Scope.Benchmark)
public class LockRoach {int x;@Benchmark@CompilerControl(CompilerControl.Mode.DONT_INLINE)public void test() {for (int c = 0; c < 1000; c++) {synchronized (this) {x += 0x42;}}}
}

(完整的源代码参见这里 ,请查看原文链接)

这里有一些重要的技巧:

  1. 使用 -XX:-UseBiasedLocking 禁用偏向锁(Biased Lock)可以避免启动时间过长。由于偏向锁不会立即启动,在初始化阶段要等待5秒钟(参见 BiasedLockingStartupDelay 选项)

  2. 禁用 @Benchmark 方法内联操作可以帮助我们从反汇编中分离相关内容

  3. 加上“魔数” 0x42 有助于快速从反汇编中定位加法操作

译注:偏向锁(Biased Locking)。尽管 CAS 原子指令相对于重量级锁来说开销比较小,但还是存在非常可观的本地延迟,为了在无锁竞争的情况下避免取锁获过程中执行不必要的 CAS 原子指令提出了偏向锁技术。
论文 Quickly Reacquirable Locks ,作者 Dave Dice、Mark Moir、William Scherer III。

运行环境 i7 4790K、Linux x86_64、JDK EA 9b156:

Benchmark            Mode  Cnt      Score    Error  Units
LockRoach.test       avgt    5   5331.617 ± 19.051  ns/op

从上面运行数据能分析出什么结果?什么都看不出来,对吧?我们需要调查背后到底发生了什么。这时 -prof perfasm 配置可以派上用场,它能显示生成代码中的热点区域。用默认设置运行,能够发现最热的指令是加锁 lock cmpxchg(CAS),而且只打印指令附近的代码。-prof perfasm:mergeMargin=1000 配置可以将这些热点区域合并保存为输出片段,乍看之下可能觉得有点恐怖。

进一步分析得出连续的跳转指令是锁定或解锁,注意循环次数最多的代码(第一列),可以看到最热的循环像下面这样:

↗  0x00007f455cc708c1: lea    0x20(%rsp),%rbx│          < 省略若干代码,进入 monitor >     ; <--- coarsened(粗化)!│  0x00007f455cc70918: mov    (%rsp),%r10        ; 加载 $this│  0x00007f455cc7091c: mov    0xc(%r10),%r11d    ; 加载 $this.x│  0x00007f455cc70920: mov    %r11d,%r10d        ; ...hm...│  0x00007f455cc70923: add    $0x42,%r10d        ; ...hmmm...│  0x00007f455cc70927: mov    (%rsp),%r8         ; ...hmmmmm!...│  0x00007f455cc7092b: mov    %r10d,0xc(%r8)     ; LOL Hotspot,冗余存储,下面省略两行│  0x00007f455cc7092f: add    $0x108,%r11d       ; 加 0x108 = 0x42 * 4 <-- 展开4次│  0x00007f455cc70936: mov    %r11d,0xc(%r8)     ; 把 $this.x 回省略若干代码,退出 monitor >      ; <--- coarsened(粗化)!│  0x00007f455cc709c6: add    $0x4,%ebp          ; c += 4   <--- 展开4次│  0x00007f455cc709c9: cmp    $0x3e5,%ebp        ; c < 1000?╰  0x00007f455cc709cf: jl     0x00007f455cc708c1

哈哈。循环似乎被展开了4次,然后这4个迭代中实现锁粗化!为了排除循环展开对锁粗化的影响,我们可以通过-XX:LoopUnrollLimit=1 配置裁剪循环展开,再次量化受限后的粗化性能。

译注:Loop unrolling(循环展开),也称 Loop unwinding,是一种循环转换技术。它试图以牺牲二进制大小为代价优化程序的执行速度,这种方法被称为时空折衷。转换可以由程序员手动执行,也可以由编译器优化。

Benchmark            Mode  Cnt      Score    Error  Units# Default
LockRoach.test       avgt    5   5331.617 ± 19.051  ns/op# -XX:LoopUnrollLimit=1
LockRoach.test       avgt    5  20679.043 ±  3.133  ns/op

哇,性能提升了4倍!显而易见的,因为我们已经观察到最热的指令是加锁 lock cmpxchg。当然,4倍后的粗化锁意味着4倍吞吐量。非常酷,我们是不是可以宣布成功,然后继续前进?还没有。我们必须验证禁用循环展开真正提供了我们想要进行比较的内容。perfasm 的结果似乎表明它含有类似的热点循环,只是跨了一大步。

↗  0x00007f964d0893d2: lea    0x20(%rsp),%rbx│          < 省略若干代码,进入 monitor >│  0x00007f964d089429: mov    (%rsp),%r10        ; 加载 $this│  0x00007f964d08942d: addl   $0x42,0xc(%r10)    ; $this.x += 0x42│          < 省略若干代码,退出 monitor >│  0x00007f964d0894be: inc    %ebp               ; c++│  0x00007f964d0894c0: cmp    $0x3e8,%ebp        ; c < 1000?╰  0x00007f964d0894c6: jl     0x00007f964d0893d2 ;

一切都检查 OK。

4. 观察结果

当锁粗化在整个循环中不起作用时,一旦中间看起来好像存在 N 个相邻的加锁解锁操作,另一种循环优化——循环展开会提供常规锁粗化。这将提高性能,并有助于限制粗化的范围,以避免长循环过度粗化。

版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。

-更多文章-

谈谈 Java 类加载机制

当当书满100减50,还可用叠加优惠码,超级实惠~

最近面试 Java 后端开发的感受!

深入了解浏览器存储

你的接口,真的能承受高并发吗?

微服务架构之「 服务注册 」

你知道Java的四种引用类型吗?

-关注我-

感谢搓一下“在看

Java 锁粗化与循环相关推荐

  1. java for循环延迟_Java 锁粗化与循环问题

    1. 写在前面 "JVM 解剖公园"是一个持续更新的系列迷你博客,阅读每篇文章一般需要5到10分钟.限于篇幅,仅对某个主题按照问题.测试.基准程序.观察结果深入讲解.因此,这里的数 ...

  2. java 7 锁优化_自Java 6/Java 7开始,Java虚拟机对内部锁的实现进行了一些优化。这些优化主要包括锁消除(Lock Elision)、锁粗化(Lock Coarse...

    自Java 6/Java 7开始,Java虚拟机对内部锁的实现进行了一些优化.这些优化主要包括锁消除(Lock Elision).锁粗化(Lock Coarsening).偏向锁(Biased Loc ...

  3. Java中的锁机制 -- 乐观锁、悲观锁、自旋锁、可重入锁、读写锁、公平锁、非公平锁、共享锁、独占锁、重量级锁、轻量级锁、偏向锁、分段锁、互斥锁、同步锁、死锁、锁粗化、锁消除

    文章目录 1. Java中的锁机制 1.1 乐观锁 1.2 悲观锁 1.3 自旋锁 1.4 可重入锁(递归锁) 1.5 读写锁 1.6 公平锁 1.7 非公平锁 1.8 共享锁 1.9 独占锁 1.1 ...

  4. Java锁优化思路及JVM实现

    1. 锁优化的思路和方法 这里提到的锁优化,是指在阻塞式的情况下,如何让性能不要变得太差.但是再怎么优化,一般来说性能都会比无锁的情况差一点. 这里要注意的是,在ReentrantLock中的tryL ...

  5. 通过踩坑带你读透虚拟机的“锁粗化”

    之前在学习volatile时,踩过一些坑.通过这些坑,学习了一些jvm的锁优化机制.后来在面试的过程中,被问到的概率还挺高.于是,我整理了这篇踩坑记录. 1. java多线程内存模型 在聊踩坑记录前, ...

  6. 面试必会系列 - 1.5 Java 锁机制

    本文已收录至 github,完整图文:https://github.com/HanquanHq/MD-Notes 面试必会系列专栏:https://blog.csdn.net/sinat_424833 ...

  7. concurrent 底层_万字长文!从底层开始带你了解并发编程,彻底帮你搞懂Java锁!

    线程是否要锁住同步资源 锁住 悲观锁 不锁住 乐观锁 锁住同步资源失败 线程是否要阻塞 阻塞 不阻塞自旋锁,适应性自旋锁 多个线程竞争同步资源的流程细节有没有区别 不锁住资源,多个线程只有一个能修改资 ...

  8. java底层编程_万字长文!从底层开始带你了解并发编程,彻底帮你搞懂Java锁!

    线程是否要锁住同步资源锁住 悲观锁 不锁住 乐观锁 锁住同步资源失败 线程是否要阻塞阻塞 不阻塞自旋锁,适应性自旋锁 多个线程竞争同步资源的流程细节有没有区别不锁住资源,多个线程只有一个能修改资源成功 ...

  9. Java锁与线程的那些“不可描述”的事儿

    一.引言 "操作系统的线程状态和java的线程状态有什么关系?"这是校招时被问到的一个问题.当时只顾着看博文.面经等零散的资料,没有形成系统的知识体系,一时语塞,答的不是很对.在网 ...

最新文章

  1. 用asp.net实现的把本文推荐给好友功能
  2. MySQL · 引擎介绍 · Sphinx源码剖析(三)
  3. Kruskal HDOJ 1233 还是畅通工程
  4. pageoffice网页提示未安装_Adobe Photoshop CC 2019 详细图文安装教程
  5. 【机器视觉】 default算子
  6. lenovo Think Centre TCM 开发环境搭建
  7. 复刻了一个史上最强 Redis 6.0 版本
  8. [导入]新网络流行语 打酱油 三个俯卧撑
  9. SQL Server 2008 Service Broker
  10. 用友nc系统服务器是云端吗,用友NC服务器硬件配置要求
  11. svn删除文件出错的经验总结
  12. 内连接、外连接和全连接的区别
  13. Android软键盘的删除键和activity返回冲突
  14. GMIS 2017大会圆桌论坛:机器智能时代的全球化进程
  15. Python实现ABC人工蜂群优化算法优化支持向量机回归模型(SVR算法)项目实战
  16. 嵌入式linux界面开发,基于嵌入式Linux平台实现GUI系统的设计方案
  17. 推荐学习方法——费曼技巧,以教促学,教学相长
  18. c# 使用Microsoft.Office.Interop.Excel 对Excel操作
  19. 听歌的时候再也不用各个音乐播放软件来回切换啦!
  20. Psins代码解析之test_SINS_east_west.mtest_SINS_north_south.m

热门文章

  1. 微信小程序实例源码大全demo下载
  2. js url传值中文乱码完美解决(JAVA)
  3. 运行报表时提示输入用户名和密码
  4. 如何成为一个好的测试工程师(转载,大意)
  5. IPMSM弱磁控制策略
  6. GridView 始终显示 Pager 分页行的一种方法
  7. 赢在中国(08-02-27)
  8. python文件和目录
  9. opencv 自带库Canny边缘检测
  10. matlab求解外弹道,基于MATLAB∕Simulink的通用质点外弹道程序设计.pdf