性能文章>死磕synchronized三:系统剖析延迟偏向篇二>

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

2056020

哈喽,我是子牙。十余年技术生涯,一路披荆斩棘从技术小白到技术总监到JVM专家到创业。技术栈如汇编、C语言、C++、Windows内核、Linux内核。特别喜欢研究虚拟机底层实现,对JVM有深入研究。分享的文章偏硬核,很硬的那种。

 

手撸过JVM、内存池、垃圾回收算法、synchronized、线程池、NIO…

 

 

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

  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源码找线索,终于找到了核心代码

 

 

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

 

04

系列文章

1、JVM如何执行synchronized修饰的方法

2、系统剖析延迟偏向篇一

 

05

推荐阅读

1、技术人如何才能拿到百万年薪?

2、为什么他们成为了技术大牛?

3、深入剖析Lambda表达式的底层实现原理

 

 

我有话说

 

我是子牙。十余年技术生涯,一路披荆斩棘从小白到技术总监到大厂中间件到创业。技术栈如汇编、C语言、C++、Windows内核、Linux内核及特别喜欢研究虚拟机底层实现,对JVM有深入研究。分享的文章偏硬核,很硬的那种。不考虑交个朋友吗?关注硬核子牙:

分类:
标签:
请先登录,再评论

暂无回复,快来写下第一个回复吧~

为你推荐

不起眼,但是足以让你有收获的JVM内存分析案例
分析 这个问题说白了,就是说有些int[]对象不知道是哪里来的,于是我拿他的例子跑了跑,好像还真有这么回事。点该 dump 文件详情,查看相关的 int[] 数组,点该对象的“被引用对象”,发现所
从一起GC血案谈到反射原理
前言 首先回答一下提问者的问题。这主要是由于存在大量反射而产生的临时类加载器和 ASM 临时生成的类,这些类会被保留在 Metaspace,一旦 Metaspace 即将满的时候,就会触发 Fu
关于内存溢出,咱再聊点有意思的?
概述 上篇文章讲了JVM在GC上的一个设计缺陷,揪出一个导致GC慢慢变长的JVM设计缺陷,可能有不少人还是没怎么看明白的,今天准备讲的大家应该都很容易看明白 本文其实很犹豫写不写,因为感觉没有
协助美团kafka团队定位到的一个JVM Crash问题
概述 有挺长一段时间没写技术文章了,正好这两天美团kafka团队有位小伙伴加了我微信,然后咨询了一个JVM crash的问题,大家对crash的问题都比较无奈,因为没有现场,信息量不多,碰到这类问题我
又发现一个导致JVM物理内存消耗大的Bug(已提交Patch)
概述 最近我们公司在帮一个客户查一个JVM的问题(JDK1.8.0_191-b12),发现一个系统老是被OS Kill掉,是内存泄露导致的。在查的过程中,阴差阳错地发现了JVM另外的一个Bug。这个B
JVM实战:优化我的IDEA GC
IDEA是个好东西,可以说是地球上最好的Java开发工具,但是偶尔也会卡顿,仔细想想IDEA也是Java开发的,会不会和GC有关,于是就有了接下来对IDEA的GC进行调优 IDEA默认JVM参数: -
不起眼,但是足以让你收获的JVM内存案例
今天的这个案例我觉得应该会让你涨姿势吧,不管你对JVM有多熟悉,看到这篇文章,应该还是会有点小惊讶的,不过我觉得这个案例我分享出来,是想表达不管多么奇怪的现象请一定要追究下去,会让你慢慢变得强大起来,
如何通过反射获得方法的真实参数名(以及扩展研究)
前段时间,在做一个小的工程时,遇到了需要通过反射获得方法真实参数名的场景,在这里我遇到了一些小小的问题,后来在部门老大的指导下,我解决了这个问题。通过解决这个问题,附带着我了解到了很多新的知识,我觉得