对象的内存布局
- 对象头(Header)
- 实例数据(Instance Data)
- 对其填充(Padding)
对象头
- Mark Word:存储对象的hashcode、分代年龄、锁信息等
- Class Metadata Address(类型指针):存储到对象类型数据的指针
- Array length:数组长度(数组特有)
实例数据
对象真正存储的有效信息,继承自父类及子类所定义的所定义的各种类型的字段内容。
对齐填充
起占位符的作用,对象的大小必须为8字节的整倍数,对象头部分正好为8字节的倍数(1倍或2倍),当对象实例数据部分没有对其时,需使用对其填充来补全。
64位虚拟机下的Mark Word(64bit)
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.11</version>
</dependency>
Object object = new Object();
int[] arr = new int[10];
System.out.println(ClassLayout.parseInstance(object).toPrintable());
System.out.println(ClassLayout.parseInstance(arr).toPrintable());
锁的状态由低到高
无锁——>偏向锁——>轻量级锁——>重量级锁
注:锁的状态随着竞争情况逐渐升级,但不能降级。
锁状态 | 锁标志位 | Mark Word |
---|---|---|
偏向锁 | 101 | 线程ID I Epoch I 101 |
轻量级锁 | 00 | 指向栈的指针 I 00 |
重量级锁 | 10 | 指向重量级锁的指针 I 00 |
偏向锁
原因:大多数情况下,锁不仅不存在竞争,且总是由同一线程多次获得。
失效:当一个线程已经获取过偏向锁,另一个线程尝试获取偏向锁时,偏向锁失效。
轻量级锁
原理:在当前线程的栈帧中创建内存复制锁的对象头Mark Word,通过CAS将栈帧中对象头Mark Word替换为执行锁记录的指针,如果替换成功再将修改锁的Mark Word,如果替换失败将升级为重量级锁。
(1)验证无锁
public class Test {
private static Object lock = new Object();
public static void main(String[] args) {
System.out.println(ClassLayout.parseInstance(lock).toPrintable());
}
}
注意:顺序是与上面的图片倒着的,有兴趣的可以用Integer.toBinaryString()将二进制后面的int值打印出来对照看一下。
(2)验证偏向锁
-XX:+UseBiasedLocking:开启偏向锁(jdk6及以上默认开启)
-XX:BiasedLockingStartupDelay=0:默认延迟5s启动(这里测试建议开启)
public class Test {
private static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
System.out.println(ClassLayout.parseInstance(lock).toPrintable());
synchronized (lock) {
System.out.println(ClassLayout.parseInstance(lock).toPrintable());
}
}
}
关闭偏向锁:-XX:-UseBiasedLocking=false
(3)验证轻量级锁
注意:以下代码可能会出现重量级锁的情况。
public class Test {
private static final Logger log = LoggerFactory.getLogger(Test.class);
private static Object o = new Object();
public static void main(String[] args) throws InterruptedException {
log.info(ClassLayout.parseInstance(o).toPrintable());
synchronized (o) {
log.info(ClassLayout.parseInstance(o).toPrintable());
}
new Thread(() -> {
synchronized (o) {
log.info(ClassLayout.parseInstance(o).toPrintable());
}
}).start();
Thread.sleep(1000000);
}
}
(4)验证重量级锁
public class Test {
private static final Logger log = LoggerFactory.getLogger(Test.class);
private static Object o = new Object();
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
synchronized (o) {
log.info(ClassLayout.parseInstance(o).toPrintable());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
}
}).start();
synchronized (o) {
log.info(ClassLayout.parseInstance(o).toPrintable());
Thread.sleep(1000);
}
Thread.sleep(1000000);
}
}