对象头与锁与synchronized
1、对象有多大
-
下述代码中o对象有多大?
Object o = new Object();
我们知道一个对象是由对象头、实例数据与对齐填充三部分组成。
1. 对象头: 1. markword:hashcode、锁状态、偏向线程ID、分代年龄等 2. 类型指针:指向对象的.class对象 2. 实例数据:对象中定义的实例变量 3. 对齐填充:将对象大小补齐至8的整数倍
当然o这个对象自然也是由这三部分组成。那么想知道o对象有多大自然要知道每一部分有多大。
1. 对象头 1. markword:8字节 2. 类型指针: 关闭指针压缩->8字节 开启指针压缩->4字节 2. 实例数据:Object对象没有实例数据即大小为0字节 3. 对齐填充:根据对象头+实例数据的大小进行填充 4. 总结: 1. 开启压缩的情况下:8+4+4=16->markword+类型指针+对齐填充 2. 关闭压缩的情况下:8+8=16->markword+类型指针
测试代码见附录1
现在我们知道了对象的结构与对象的大小,那么对象与锁又有什么关系呢?
2、对象与锁
1、简述
锁有不同的分类。分别是:无锁态、偏向锁、轻量级锁 (自旋锁,自适应自旋)、重量级锁。(这里只需要了解有多少种不同的锁分类即可,不要刻板的认为new出来的对象下一步会升级为偏向锁。)锁的相关信息必然是要被记录的而记录的载体正是对象的对象头中的markword。此外由于存储空间的有限,为了节省空间,提高空间利用率我们将markword这8个字节根据不同的锁状态划分成了不同的结构。其中锁状态与markword的关系入下图所示。
2、锁概述
1、无锁态
对于jkd1.8来说一个对象new出来默认是无锁态的。其markword中存储了hashcode、分代年龄、偏向锁位、锁标志位。其中当我们调用hashcode方法后对象中才会有hashcode的数据。当其被加锁后则变为轻量锁。
hashcode测试代码见附录2
无锁态升级轻量锁测试代码见附录3
2、偏向锁
偏向锁是对锁的一种优化,在JDK11中new出来的对象默认是偏向锁。其分为匿名偏向锁与偏向锁。当一个对象没有被锁住时便是匿名偏向锁。如果被锁住那么,就会在对象的markword中就会记录下当前线程的指针。如果在运行时此锁没有被争夺那么持有偏向锁的线程就不用进行同步。
3、轻量锁
轻量级锁又叫自旋锁,它通过自旋的方式(CAS)来切换锁的所有者,当自旋的线程较多时会升级为重量锁。
CAS:在改变数据时先拿取数据变量a的值E,然后计算出V,再用E和a的值N进行比较要是相等则更新数值不相等则 重新读取数据再计算。
ABA问题:线程1先读取的变量a的值E,然后计算出V,在此期间线程2对变量a进行了加1与减1操作。此时线程1 无法得知a的值发生过变化,这就叫ABA问题。当然我们只需要给数值加上一个版本号就可以解决这个问题。
4、重量锁
重量锁由操作系统实现。会导致由内核态转为用户态,消耗更多的资源。当对象升级为重量锁时争夺的线程进入队列进行等待。
3、锁实现
1、java代码实现:synchronized
2、字节码:monitorenter monitorexit
3、运行时锁自动升级
3、汇编:lock comxchg
4、锁升级
1、基本路线一:初始为偏向锁
匿名偏向锁->偏向锁->轻量锁->重量锁
1、初始状态下为偏向锁
2、对象被锁住时进入偏向锁状态
3、当对象被争夺时进入轻量锁状态
4、当竞争比较激烈时进入重量锁状态
2、基本路线二:初始为无锁态
new->轻量锁->重量锁
1、初始状态为无锁态
2、当对象被争夺时进入轻量锁状态
3、当竞争比较激烈时进入重量锁状态
5、synchronized的使用
附录
前置任务:加载对应工具类包
<dependencies>
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
</dependencies>
附录1:对象头大小
public static void main(String[] args) throws InterruptedException {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
附录2:对象hashcode
public static void main(String[] args) throws InterruptedException {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
o.hashCode();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
附录3:无锁态升级轻量锁
public static void main(String[] args) throws InterruptedException {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
synchronized (o) {
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}