学习笔记---锁的膨胀升级

tech2022-08-24  109

类加载机制

锁的类型存放地 ---markWorld#HashCode信息偏向锁信息轻量锁与重量锁信息锁标志位是否允许偏向锁 标志位什么时候可以偏向?什么时候不能偏向? 锁的加载偏向锁轻量锁重量锁 关于锁升级的类比----之家里有个胖厨师

锁的类型存放地 —markWorld

markworld 是对象头的一部分,他的主要功能是 存储 gc标志,是否可以偏向,锁的类型,hashcode,分代年龄,以及与锁有关的线程信息,所以要了解所得升级先要了解markWord是什么样的

markworld 以二进制的形式总共的存储64bit

#HashCode信息

位于markworld的26bit~56bit 在计算过hashcode之后,hashcode信息会记录在markworld上

偏向锁信息

位于markworld的1bit~54bit 在允许偏向线程的情况下,首个线程进入对象时记录相关进程信息

轻量锁与重量锁信息

位于markworld的1bit~62bit 在发生竞争后,记录当前持锁线程的信息

锁标志位

锁标志位在第63~64bit(二进制) 是记录锁的类型 01:偏向锁 00:轻量锁 10:重量锁 11:gc标志

性能方面 偏向锁>轻量级锁>重量级锁

是否允许偏向锁 标志位

位于偏向锁的62bit 表示这个对象可不可被偏向 1:表示可以被偏向 0:表示不能被偏向 无锁状态:偏向锁标志为0 ,锁状态为01

什么时候可以偏向?什么时候不能偏向?

由于偏向锁信息 占有了54个bit,并且单个线程的情况下偏向线程信息不会发生改变。 而hashCode 却占了31位, 偏向锁线程信息与hashCode的存储位置会发生了冲突。 所以在计算hashcode之后,由于没有足够的空间存储偏向锁信息,所以偏向锁标志就变为不可偏向。 值得一提的是,如果记录偏向锁之后再计算hashcode,那么偏向锁会膨胀成重量锁。

锁的加载

偏向锁

在对象没有初生成的时候,是处于匿名偏向(可偏向但是没有持有偏向锁)状态 加锁开始后,jvm会在栈帧中创建个lockRecord,并将lockRecord中的objectRef指向加锁对象

在线程中生成偏向自己的header通过cas操作将线程信息存入对象的markWorld当中,此时的对象会记录下第一个线程的线程信息, 锁标志位不变,还是101

偏向锁加锁为什么快? 因为在相同的线程第二次进来后,jvm 会在几个简单的if判断(对象是否是允许偏向,是否偏向当前线程,对象是否过期)后就可以执行代码块,所以线程第二次进入后偏向锁的的性能极高

偏向撤销 需要等待安全点的出现,暂停并检查持有偏向锁的线程,如果偏向线程存活,则执行完相关栈,并且遍历所有的锁记录,将锁记录和markworld 重新偏向或者变为无锁。如果不存活,那就简单了,直接将markWorld 改为无锁状态就好。 --------由此可见偏向锁撤销对性能是有很大的损害的

偏向锁的缺点 偏向锁虽然带来了极高的性能,但是当撤销偏向锁的时候,却会带来的性能上的损耗(),所以许多的jvm都设有偏向延迟(默认4秒)

批量撤销,过期 当一个类 被实例化了多次,并且所有的类都被相同线su程持有偏向锁后 此时b线程进来拿锁,因为是不同线程,锁会升级成轻量锁,但是在20个对象都被b线程持有锁后(类的偏向锁被撤销了20次),这时markworld当中代表着过期 eq会发生变化,即过期,jvm会将重新降级成偏向当前加锁线程的偏向锁

随后该类的对象被c线程加锁多次,那么再次撤销20次之后,之后新生成的类的对象,将默认是轻量锁,并且不会在被降级

轻量锁

什么时候进入轻量锁?

当对象已经有偏向线程后,另一个线程来对对象加锁。 对象中的偏向锁会撤销,新的线程将持有轻量锁

轻量锁 加锁全过程 和偏向锁一样,jvm会在栈帧中创建个lockRecord,并将lockRecord中的objectRef指向加锁对象,并将markWorld复制到锁记录当中。

线程对对象加锁后,jvm会先判断当前对象是否可以执行偏向操作(对允许偏向,过期,匿名,偏向本线程)进行判断,在内存中生成偏向当前线程的header部,然后通过cas修改,因为markworld不是偏向当前线程,所以加锁失败,进入轻量加锁流程

(1)撤销偏向锁(撤销完成后markWorld处在无锁状态 标志位001) (2)在堆中生成个无锁的markWordld对象(标志位 001) (3)通过cas的原子操作 将新生成的markWorld 与对象中的markWorld做比较,如果相等,则替换markWorld为指向 lockRecord指针,标志位变为(001 -》000) 加锁完成 (4)执行完代码块。会释放锁。将对象重新变回无锁状态(001)

轻量锁都是交替执行的,因为一旦撞车,就会升级成为重量锁

重量锁

重量锁的场景就很简单了,发生了抢锁。以及对象计算了hashcode 也会升级成重量锁

膨胀重量锁,其实是cpu从用户态升级成内核态。因为可能会调用操作系统里park方法。

在锁膨胀成重量锁之后,并不会马上阻塞,而是开始自旋,当自旋多次依旧无果后,才调用park方法

关于锁升级的类比----之家里有个胖厨师

(1)胖厨师(新的对象)刚来的时候其实不知道主人喜欢什么(匿名偏向) (2)第一天(第一个线程加锁)主人(线程1号)说喜欢川菜(加锁) (3)于是从那之后胖厨师的采购单上都准备这川菜的材料(加锁后,偏向锁偏向“线程1号”)。 (4)之后几天 主人对胖厨师能够快速的上菜表示满意(偏向锁后,同线程性能快) (5)这天来了女主人(线程二号),胖厨师(偏向线程 “1”号),不得不把联系订货商将定好的川菜食材给取消了(偏向撤销),等待女主人的吩咐(处在无锁状态) (6)女主人(线程二号)喜欢吃甜,于是胖厨师准备了甜点(标志位处在轻量锁状态) (7)女主人吃完后,胖厨师就没有去准备川菜的材料,而是等待主人们的命令(处在无锁状态,不会降级成偏向锁) (8)有一天男主人(线程1号)想吃川菜了(轻量锁加锁),胖厨师在做饭的过程中,女主人(线程二号)要吃甜点(线程二号尝试抢锁) (9)胖厨师由于没空(线程2号抢锁失败) (10)于是将女主人的请求向后推迟了(线程2号进入sleep) (11)女主人很生气,和男主人吵起来了(膨胀成重量锁)

最新回复(0)