对象头组成
32位虚拟机对象头markword
64位虚拟机对象头markword
现在我们虚拟机基本是64位的,而64位的对象头有点浪费空间,JVM默认会开启指针压缩,所以基本上也是按32位的形式记录对象头的。
对象头打印输出
引入对象头分析工具
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
打印markword
public static void main(String[] args) {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
输出
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)//markword
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)//markword
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)////Klass Pointer
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
锁升级过程
模拟无锁->偏向锁(同一个线程加锁)
直接new的对象,预期:锁状态应该为无锁,对应上图最后三位应该为001,不符合预期。
00000001 00000000 00000000 00000000
此处需要了解字节序的概念:
字节序:指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序,有大端
和小端
两种方式
大端:指高位字节存放在内存的低地址端,低位字节存放在内存的高地址端。十六进制整数0x01020304
在内存中存储方式,由于一个整型在内存中占4个字节,因此被划分成4份分别存储在连续的内存地址位中
小端:指低位字节放在内存的低地址端,高位字节放在内存的高地址端。同样0x01020304
在内存中的存储方式为
在计算机中既可以是大端
存储,也可以小端
存储,跟CPU架构有关,IA架构(Intel、AMD)的CPU中是Little-Endian,而PowerPC 、SPARC和Motorola处理器是Big-Endian
基于Java8
,在Java中提供了一个api可以获取CPU使用的字节序:
ByteOrder byteOrder = ByteOrder.nativeOrder();
System.out.println(byteOrder);////输出LITTLE_ENDIAN,CPU是Intel的
故:
00000001 00000000 00000000 00000000 =>
00000000 00000000 00000000 00000001 //符合预期
加入synchronized代码块
public static void main(String[] args) {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
synchronized (o){
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
输出:
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 28 f8 8d 02 (00101000 11111000 10001101 00000010) (42858536)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
预期:偏向锁
实际:00101000 后两位00 ,轻量级锁状态 不符合预期
原因:JVM会延迟启动偏向锁,本身jvm启动时,加载自身系统资源,会有很多sync块,本身会有线程竞争,为了减少锁升级的开销,默认延迟4秒启动偏向锁。
故休眠5秒之后开始打印
TimeUnit.SECONDS.sleep(5);
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
synchronized (o){
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
输出:
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 38 c6 02 (00000101 00111000 11000110 00000010) (46544901)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
分析:忽略大小端,直接看头8位
未开启偏向锁 无同步块 001:无锁状态
00000001 00000000 00000000 00000000
开启偏向锁 无同步块(直接输出object) 101:偏向锁 但是偏向线程id为空
00000101 00000000 00000000 00000000
开启偏向锁 加了同步块 101:偏向锁 偏向线程id有值
00000101 00111000 11000110 00000010
结论:开启偏向锁之后,即使没有任何同步块,锁状态改为偏向锁对应,后三位101,此时只改了状态,没有偏向threadid,故也称为匿名偏向,可偏向状态。
模拟偏向锁升级到轻量级锁(线程交替执行)
public static void main(String[] args) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Object o = new Object();
log.info(ClassLayout.parseInstance(o).toPrintable());
new Thread(()->{
synchronized (o){
log.info(ClassLayout.parseInstance(o).toPrintable());
}
}).start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info(ClassLayout.parseInstance(o).toPrintable());
new Thread(()->{
synchronized (o){
log.info(ClassLayout.parseInstance(o).toPrintable());
}
}).start();
}
输出
10:48:59.233 [main] INFO com.alau.T0_BasicLock - java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)//匿名偏向
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
10:48:59.236 [Thread-0] INFO com.alau.T0_BasicLock - java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 b0 3e 29 (00000101 10110000 00111110 00101001) (691974149)//偏向锁,已有theadid
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
10:49:01.248 [main] INFO com.alau.T0_BasicLock - java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 b0 3e 29 (00000101 10110000 00111110 00101001) (691974149)//偏向锁,已有theadid。第一个线程结束。记录的还是第一个线程获得锁之后的状态,和上面一样
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
10:49:01.250 [Thread-1] INFO com.alau.T0_BasicLock - java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 70 f4 80 29 (01110000 11110100 10000000 00101001) (696317040)//升级为轻量级锁
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
Process finished with exit code 0
结论:第一个线程执行结束,才执行第二个,线程交替执行,升级为轻量级锁。
模拟升级到重量级锁monitor锁
public static void main(String[] args) throws InterruptedException {
Thread.sleep(5000);
Object a = new Object();
Thread thread1 = new Thread(){
@Override
public void run() {
synchronized (a){
System.out.println("thread1 locking");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
try {
//让线程晚点儿死亡,造成锁的竞争,跳过自旋的过程
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread thread2 = new Thread(){
@Override
public void run() {
synchronized (a){
System.out.println("thread2 locking");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread1.start();
thread2.start();
}
输出
thread1 locking
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 0a f5 2b 26 (00001010 11110101 00101011 00100110) (640414986)//后两位10,重量级锁
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
thread2 locking
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 0a f5 2b 26 (00001010 11110101 00101011 00100110) (640414986)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
结论:线程激烈竞争,直接升级重量级锁。