liuxiaoshui
发布于 2023-11-23 / 22 阅读
0
0

java对象头锁状态分析及锁的升级记录

对象头组成

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

结论:线程激烈竞争,直接升级重量级锁。


评论