性能专题>深入理解 synchronized>
1
1

深入理解 synchronized分享专题

死磕synchronized三:系统剖析延迟偏向篇二

 

关于分析偏向锁延迟策略:

  1. 什么是延迟偏向

  2. 为什么需要延迟偏向

  1. 延迟偏向机制是怎样的

  2. 延迟偏向对锁膨胀的影响及证明

  1. 从Hotspot源码角度证明

 

上篇已经分享了绝大部分内容,本篇就把剩下的补上。本篇文章是从Hotspot源码角度分析延迟偏向机制:

  1. 新创建的对象的锁是是如何被延迟偏向影响的

  2. 延迟偏向之前加载的类的初始锁是什么锁

  3. 延迟偏向之后加载的类是无锁还是偏向锁

 

01

对象初始锁

新创建的对象的初始锁是如何确定的,直接看Hotspot源码

 

 

JVM创建对象的步骤是先申请内存,然后对这块内存进行数据填充。数据填充包括设置对象头。这个方法就是在数据填充阶段调用的。代码逻辑很简单,如果启用了偏向锁,则将这个oop对应的klass的属性property_header作为初始锁填充进去。如果没有启用偏向锁,则生成一个无锁作为初始锁填充进去。

 

免得有小伙伴没看过我之前的文章或视频,不知道oop与klass,做个简单的解释:Hotspot主要部分是由C++编写的,oop就是Java对象对应的C++实例,klass就是Java类对应的C++实例。

 

正常情况下偏向锁都是开启的,那创建的对象的初始锁最终受限于延迟偏向,再细化一点说是受限于创建对象基于的这个klass是在延迟偏向之前加载的还是之后。接下来咱们就看看下面三种情况底层是如何实现的:

  1. 基于延迟偏向之前加载的类,在延迟偏向之前创建的对象初始锁是无锁

  2. 基于延迟偏向之前加载的类,在延迟偏向之后创建的对象是偏向锁

  3. 基于延迟偏向之后加载的类,创建的对象都是偏向锁

 

02

延迟偏向前

首先,延迟偏向之前所有加载的类默认是无锁,所以延迟偏向之前所有创建的对象都是无锁,核心代码如下

 

 

那延迟偏向之前加载的类,延迟偏向之后创建的对象为什么是偏向锁呢?看代码

 

 

JVM在启动的时候调用了BiasedLocking::init,这个方法会创建一个VM_Operation塞入VMThread的任务队列中,经历偏向延迟后才会被执行。执行的内容就是遍历之前加载的所有的类,将它们的属性property_header改为偏向锁,即biased_locking_prototype。

 

VMThread在执行这个方法期间,就是还在执行classes_do方法,还未将_biased_locking_enabled设为TRUE,这个时候发生了类加载,那这时候加载的类生成的对象就永远是无锁状态了。为了这个问题我还特意梳理了下代码,发现JVM为了避免这个问题,执行这个方法之前是会启用安全点创造STW环境的。

SafepointSynchronize::begin();
evaluate_operation(_cur_vm_operation);

 

03

延迟偏向后

新加载的类默认都是无锁,如果是延迟偏向之前加载的类,会在延迟偏向之后,由VMThread将其改为偏向锁,从而影响基于这个类创建的对象的锁状态。那基于延迟偏向之后加载的类创建的对象是偏向锁,是为什么呢?

 

第一种可能是:加载的类是无锁,已经过了延迟偏向时间了,就不会有其他程序来改类的锁状态,所以是在创建对象填充的时候修改的。这个其实从上面介绍新创建的对象的初始化填充就已经可以正式了。所以这种可能pass。

 

所以只剩这种可能,加载的类的锁在加载期间被改掉了,改成了偏向锁,按照这个分析我单步调试Hotspot源码找线索,终于找到了核心代码

 

 

至此,延迟偏向相关的知识点就全部给大家讲完了。

请先登录,感受更多精彩内容
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步