使用JOL观测默认偏向锁

本文介绍了如何使用Java Object Layout(JOL)库来观察和理解Java对象的内存布局,特别是对象头的MarkWord和KlassPoint部分。通过Maven引入JOL库,并展示了如何解析对象内部信息、外部信息以及计算对象占用空间。文章还详细解释了在64位JVM下,开启和未开启偏向锁时对象头的MarkWord值,并提供了测试代码以展示偏向锁的状态变化。
摘要由CSDN通过智能技术生成

一、对象头

对象头有2个部分构成,一个是MarkWord,一个是KlassPoint。MarkWord记录对象的锁状态、分代年龄等对象信息。以下为64bitJVM的对象头MarkWord信息:
具体关于对象内存布局信息的知识,可以参见:Java对象的内存布局
本文主要介绍如何使用JOL去观察
在这里插入图片描述

二、使用Maven导入JOL相关Jar包

pom.xml

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

测试:

    public static void main(String[] args) throws IOException, InterruptedException {
        Integer i = Integer.valueOf(5);
        log.debug(ClassLayout.parseInstance(i).toPrintable());
    }

输出:

10:33:17.678 c.TestBiased [main] - java.lang.Integer 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)                           ae 22 00 20 (10101110 00100010 00000000 00100000) (536879790)
     12     4    int Integer.value                             5
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

常用方法:
(1)查看对象内部信息: ClassLayout.parseInstance(obj).toPrintable()

(2)查看对象外部信息:包括引用的对象:GraphLayout.parseInstance(obj).toPrintable()

(3)查看对象占用空间总大小:GraphLayout.parseInstance(obj).totalSize()

三、观察分析

1. 对象头格式

如果开启了偏向锁(默认开启),那么对象创建后,markword 值为 0x05 即最后 3 位为 101,这时它的
thread、epoch、age 都为 0
在这里插入图片描述

2. 测试观察

因为偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加 VM 参数 -XX:BiasedLockingStartupDelay=0 来禁用延迟
如果没有开启偏向锁,那么对象创建后,markword 值为 0x01 即最后 3 位为 001,这时它的 hashcode、age 都为 0

    public static void main(String[] args) throws IOException, InterruptedException {
        test1();
    }
    // 测试偏向锁
    private static void test1() {
        Dog d = new Dog();
        //偏向锁延迟,所以创建完对象之后还是无锁状态
        log.debug(ClassLayout.parseInstance(d).toPrintable());
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //延迟4s之后,输出偏向锁状态
        log.debug(ClassLayout.parseInstance(new Dog()).toPrintable());
    }
    
	class Dog {
	}
10:33:17.673 c.TestBiased [main] - cn.itcast.n4.Dog 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)                           a4 8b 01 20 (10100100 10001011 00000001 00100000) (536972196)
     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:33:22.679 c.TestBiased [main] - cn.itcast.n4.Dog 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)                           a4 8b 01 20 (10100100 10001011 00000001 00100000) (536972196)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

这里使用JOL打印的数据是以字节为单位进行逆序输出的。比如说:
在开启偏向锁的情况下MarkWord的最后3位应该是101,根据对象头的格式,前8个字节为MardWord(在64位JVM下)。
所以应该为:

      0     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000101) (5)

倒序输出,为:

      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)

也可以通过KlassPoint的内容进行具体验证。如下:

      8     4        (object header)                           a4 8b 01 20 (10100100 10001011 00000001 00100000) (536972196)

在这里插入图片描述
可以看到,真实的十六进制的KlassPoint为: 20 01 8b a4,但是打印输出为:a4 8b 01 20

注:至于为什么64位虚拟机的KlassPoint为32bit而不是64bit,是因为JVM默认开启了地址压缩
通过打印默认参数可以观察到:

java -XX:+PrintCommandLineFlags -version

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值