synchronized锁升级示例演示

2 篇文章 0 订阅

在说synchronized升级之前,先说明一下java对象的内存布局分为三部分:对象头、实例数据,对齐填充;其中对象头中含有对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志位、线程持有的锁、偏向线程ID等。在不同的锁状态标志位下有不同的差别,如下图所示:
在这里插入图片描述
如何查看对象占用内存的情况呢?OpenJDK 提供了一个非常好用的工具,JOL包,其maven依赖如下:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.13</version>
</dependency>

我们测试如下代码:

public static void main(String[] args) {
    Head head = new Head();
    System.out.println(ClassLayout.parseInstance(head).toPrintable());
}
// 定义一个类,用于获取对象的内存结构
static class Head {
}

输出结果如下图,其中锁状态标志位为01-无锁:

com.make.study.sync.JOLTest$Head 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)                           43 c1 00 20 (01000011 11000001 00000000 00100000) (536920387)
     12     4        (loss due to the next object alignment)
// 对象实例总共占用16个字节
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

下面我们再来验证一下偏向锁,验证偏向锁需要增加一个jvm运行参数:

//关闭延迟开启偏向锁,jvm默认值是4000
-XX:BiasedLockingStartupDelay=0

偏向锁代码示例如下,启动一个线程,对代码块进行加锁,然后打印锁head对象:

static Head head = new Head();
public static void main(String[] args) throws Exception {
    System.out.println(ClassLayout.parseInstance(head).toPrintable());
    Thread t1 = new Thread(() -> {
        testSync();
    });
    t1.start();
    t1.join();
}

public static void testSync() {
    synchronized (head) {
        System.out.println(ClassLayout.parseInstance(head).toPrintable());
    }
}
static class Head {}

输出结果如下,其中锁标志位01,是否偏向锁标志为1:

com.make.study.sync.SyncTest$Head 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)                           96 ac 01 20 (10010110 10101100 00000001 00100000) (536980630)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

com.make.study.sync.SyncTest$Head object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 70 f9 1b (00000101 01110000 11111001 00011011) (469331973)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           96 ac 01 20 (10010110 10101100 00000001 00100000) (536980630)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

接下来我们再看看轻量级锁,看下面这个示例:

static Head head = new Head();
public static void main(String[] args) throws Exception {
	// 执行代码之前先调用head的hashcode方法
    System.out.println(Integer.toHexString(head.hashCode()));
    System.out.println(ClassLayout.parseInstance(head).toPrintable());
    Thread t1 = new Thread(() -> {
        testSync();
    });
    t1.start();
    t1.join();
}

public static void testSync() {
    synchronized (head) {
        System.out.println(ClassLayout.parseInstance(head).toPrintable());
    }
}
static class Head {
}

从示例代码中可以看到,第一行先调用了head的hashCode方法,但是锁状态标志位并不是01,而是00-轻量级锁,因此我们得出结论,若先调用锁对应的对象的hashCode方法后,锁直接会升级为轻量级锁,结果如下:

// 这里打印的是head对象对应hashCode的16进制值
34340fab
com.make.study.sync.SyncTest$Head object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 ab 0f 34 (00000001 10101011 00001111 00110100) (873442049)
      4     4        (object header)                           34 00 00 00 (00110100 00000000 00000000 00000000) (52)
      8     4        (object header)                           96 ac 01 20 (10010110 10101100 00000001 00100000) (536980630)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

com.make.study.sync.SyncTest$Head object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           b8 ef 1f 1d (10111000 11101111 00011111 00011101) (488632248)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           96 ac 01 20 (10010110 10101100 00000001 00100000) (536980630)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

最后我们再来看看重量级锁的代码示例:

static Head head = new Head();

public static void main(String[] args) throws Exception {
    System.out.println(ClassLayout.parseInstance(head).toPrintable());
    Thread t1 = new Thread(() -> {
        testSync();
    });
    Thread t2 = new Thread(() -> {
        testSync();
    });
    t1.start();
    t2.start();
    t1.join();
    t2.join();
}

public static void testSync() {
    synchronized (head) {
        System.out.println(ClassLayout.parseInstance(head).toPrintable());
    }
}

static class Head {
}

输出结果如下,两个线程打印出的锁监视器对象的锁状态标志位为10,即重量级锁:

com.make.study.sync.SyncTest$Head 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)                           96 ac 01 20 (10010110 10101100 00000001 00100000) (536980630)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

com.make.study.sync.SyncTest$Head object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           ea f7 61 1a (11101010 11110111 01100001 00011010) (442628074)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           96 ac 01 20 (10010110 10101100 00000001 00100000) (536980630)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

com.make.study.sync.SyncTest$Head object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           ea f7 61 1a (11101010 11110111 01100001 00011010) (442628074)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           96 ac 01 20 (10010110 10101100 00000001 00100000) (536980630)
     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对象内存布局与访问定位

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值