代码分析sychronied和Mark Word的关系
在JDK1.6及之后的版本中,synchronized锁得到了优化,引入了自适应自旋锁、偏向锁、轻量锁。避免了一上来就加重量级锁,优化了锁在一定条件下的性能。
在hotspot虚拟机中,对象头主要包括两部分 MarkWord和Klass Pointer。
-
MarkWord 对象标记字段,默认存储的是对象的HashCode,GC的分代年龄(2bit最大表示15)和锁的标志信息等。对于32位的虚拟机MarkWord占32bit,对于64位的虚拟机MarkWord占用64字节。
-
Klass Pointer Class 对象的类型指针,它指向对象对应的Class对象的内存地址。大小占4字节(指针压缩的情况下为4字节,未进行指针压缩则占8字节)。32位虚拟机MarkWord分布
1.Mark Word结构
基本都使用64位JVM,不在对32位进行描述。Mark Word 在64位 JVM 中结构如下图所示(转自:duanmy0687):
2.锁升级过程
synchronized的锁升级过程中,锁状态升级过程为:无锁状态–> 偏向锁状态—>轻量级锁状态—>重量级锁状态。
2.0.前言:
锁的标志为如下红框标志:
偏向锁标识位 | 锁标识位 | 锁状态 | 存储内容 |
---|---|---|---|
0 | 01 | 未锁定 | hash code(31),年龄(4) |
1 | 01 | 偏向锁 | 线程ID(54),时间戳(2),年龄(4) |
无 | 00 | 轻量级锁 | 栈中锁记录的指针(64) |
无 | 10 | 重量级锁 | monitor的指针(64) |
无 | 11 | GC标记 | 空,不需要记录信息 |
2.1.无锁状态
当一个对象刚开始new出来时,该对象是无锁状态。此时偏向锁位为0,锁标志位01。
import org.openjdk.jol.info.ClassLayout;
/**
* @Author tangjunjie
* @Date 2022/8/3 9:54
* @PackageName:面试
* @ClassName: Test
* @Description: TODO
* @Version 1.0
*/
public class Test {
public static void main(String[] args) {
Test test = new Test();
System.out.println(ClassLayout.parseInstance(test).toPrintable());
}
}
2.2.升级为偏向锁状态
如果有线程上锁:指的就是把markword的线程ID改为自己线程ID的过程。
public class Test2 {
public static void main(String[] args) {
Test test = new Test();
new Thread(()->{
synchronized(test){
System.out.println(ClassLayout.parseInstance(test).toPrintable());
}
}).start();
}
}
2.3.升级为轻量级锁状态
如果有线程竞争:撤销偏向锁,升级轻量级锁。线程在自己的线程栈生成LockRecord,用CAS操作将markword设置为指向自己这个线程的LockRecord的指针,设置成功者得到锁。
public class Test3 {
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Thread thread = new Thread(()->{
synchronized (test) {
System.out.println(ClassLayout.parseInstance(test).toPrintable());
}
});
thread.start();
//等待thread
thread.join();
synchronized (test) {
System.out.println(ClassLayout.parseInstance(test).toPrintable());
}
}
}
2.4.升级为重量级锁状态
如果竞争加剧
竞争加剧:有线程超过10次自旋, -XX:PreBlockSpin,或者自旋线程数超过CPU核树的一半,1.6之后,加入自适应自旋adapative self spinning,JVM自己控制;
升级重量级锁: 向操作系统升级资源,等待操作系统的调度,然后再映射会用户空间;
public class Test3 {
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Thread thread = new Thread(()->{
synchronized (test) {
System.out.println(ClassLayout.parseInstance(test).toPrintable());
}
});
thread.start();
//等待thread
//thread.join();
synchronized (test) {
System.out.println(ClassLayout.parseInstance(test).toPrintable());
}
}
}