Java对象头与偏向锁、轻量级锁

声明

本文是笔者在阅读周志明老师的《深入理解Java虚拟机》后,将相关知识点思考、转化为自己的理解并输出,如果有理解不到位的地方,欢迎指正!

1 Java对象头

在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头、实例数据和对齐填充。本文重点介绍对象头的Mark Word。
HotSpot虚拟机对象的对象头部分包括两类信息(如果是数组对象则是三类)。第一类是用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、是否偏向锁等,官方称它为"Mark Word"。Mark Word被设计成一个有着动态定义的数据结构,可以根据对象的状态复用自己的存储空间。
第二类是类型指针,即对象指向它的类型元数据的指针,Java虚拟机通过这个指针来确定该对象是哪个类的实例,官方称它为"Klass Word"。
此外,如果是数组对象,那么在对象头中还有一块用于记录数组长度的内存,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但如果数组长度是不确定的,将无法通过元数据中的信息推断出数组大小(这就是Java对象所需内存的大小在类加载完成后便可以完全确定的原因)。
对象头结构图

1.1 Mark Word

在32和64位的HotSpot虚拟机中,Mark Word分别占32和64bit,本文以64bit为例讲解。
Mark Word

2 锁优化

2.1 偏向锁

2.1.1 偏向锁的介绍

偏向锁是JDK6中引入的一项锁优化措施,它的目的是消除数据在无竞争情况下的同步原语。因为大多数时候都是不存在竞争的,通常都是一个线程在使用锁的时候没有其他线程来竞争,如果每次都进行加锁和解锁就会额外增加一些没有必要的资源浪费,所以引入了偏向锁。偏向锁中的“偏”就是偏心的“偏”,它的意思是这个锁会偏向于第一个获得他的线程,如果在接下来的执行过程中,该锁一直没有被其他线程获取,则持有偏向锁的线程将永远不需要再进行同步。

2.1.2 偏向锁的获取

1)当一个线程A在执行同步块时,线程A首先会获得该对象的Mark Word,通过偏向模式来判断偏向锁是否可用。如果不可用则直接进入轻量级锁获得过程。
2)如果可用,则判断当前对象的Mark Word中存储的线程ID是否指向当前线程,如果指向当前线程,说明已经获得过锁,继续执行剩余代码即可。
3)如果没有指向当前线程,则通过CAS操作尝试将当前线程ID记录在对象的Mark Word中,若CAS操作成功,即获得锁,继续执行其余代码,若CAS操作不成功,表示存在其他线程竞争此锁,此时进行偏向锁撤销,升级为轻量级锁获取过程。

2.1.3 偏向锁的撤销

偏向锁使用了一种等到竞争出现才释放锁的机制,所以当有其他线程尝试竞争锁时,持有偏向锁的线程才会释放锁。
1)在一个全局安全点暂停拥有锁的线程A(全局安全点指当前没有字节码运行)。
2)判断线程A是否存活,如果没有存活,则将偏向锁撤销为无锁,线程B继续竞争该锁。
3)如果线程A存活,判断线程A是否继续竞争锁,如果不竞争,线程B获得偏向锁,如果竞争,则升级为轻量锁。
4)如果线程A存活,则判断对象Mark Word中的Epoch值(偏向锁Epoch字段表示此对象偏向锁的撤销次数,默认值是40,如果大于40表示当前对象不再适合偏向锁,当线程下次获取此对象时,直接变为轻量锁),如果大于40,直接升为轻量锁。
偏量锁

2.2 轻量级锁

2.2.1 轻量级锁的介绍

轻量锁是JDK6时加入的新型锁机制,是相对于使用操作系统互斥量来实现的传统锁而言的,因此传统的锁机制就被称为重量级锁。轻量级锁是当线程A获取到锁后,线程B也来获取锁对象,线程B并不会被直接阻塞,而是通过自适应自旋来等待该锁被释放。JVM引入轻量级锁来减少传统的重量级锁使用操作系统互斥量产生的性能消耗。

2.2.2 轻量级锁的获取

在线程A访问同步代码块之前,虚拟机会在当前线程A的栈帧中建立一个名为锁记录的空间,并且将对象头中的Mark Word拷贝到该所记录中,然后虚拟机使用CAS操作尝试把对象的Mark Word更新为指向锁记录的指针。如果CAS操作成功,即代表该线程拥有了这个对象的锁,开始执行同步代码即可。如果CAS操作失败,那么就意味着存在多线程竞争锁的情况,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是说明当前线程已经拥有了这个对象的锁,那么直接进入同步代码块继续执行代码就行,否则说明这个锁对象已经被其他线程抢占了,此时锁膨胀为重量级锁,此时Mark Word中存储的就是指向重量级锁的指针,后面等待的线程也必须进入阻塞状态。

2.2.3 轻量级锁的撤销

当获取锁的线程执行完毕,释放锁通过CAS操作将对象头中的信息重新替换还回去,如果CAS操作成功,则成功释放锁,整个同步过程顺利完成,如果CAS操作失败说明存在其他线程竞争此锁,此时锁已经膨胀为重量级锁,就要在释放锁的同时,唤醒被挂起的线程。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42400040/article/details/106868524
今日推荐