一、对象头
对象头有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