JOL简介
JOL(Java对象布局)是用于分析JVM中对象布局方案的微型工具箱。这些工具大量使用
Unsafe
、JVMTI
和可服务性代理
(SA)来解码实际的对象布局、示意图和引用。JOL 的官方文档openjdk.java.net/projects/co…
JOL常用方法
//1.使用jol计算对象的大小(单位为字节):
ClassLayout.parseInstance(obj).instanceSize();
//2.使用jol查看对象的内存布局:
ClassLayout.parseInstance(obj).toPrintable();
Mark Word的结构信息
JOL打印信息分析
OFF:偏移地址,单位字节。
SIZE:占用的内存大小,单位为字节。
TYPE DESCRIPTION: 类型描述。
(object header: mark)为对象头的
Mark Word
(标记字段)。(object header: class)为对象头的
Class Pointer
(类型指针)。(object alignment gap)因为在 64 位虚拟机上对象的大小必须是 8 的倍数)。 即:
Mark Word
的SZ
+Class Pointer
的SZ
+gap
的SZ
=n*8
。VALUE : 对应内存中当前存储的值。
Instance size:实例字节数值大小。
Space losses:空间损失的计算。
JOL的使用案例
引入jol依赖
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
创建对象示例
@Slf4j
public class Test {
public static void main(String[] args) {
Person person = new Person();
log.info(ClassLayout.parseInstance(person).toPrintable());
log.debug("===========调用对象的HashCode后==========");
person.hashCode();
log.info(ClassLayout.parseInstance(person).toPrintable());
}
}
class Person {
private int i;
private long j;
private Student student;
}
class Student {
private int k;
}
//执行结果:
14:41:37.456 [main] INFO com.wuke.test.bigdecimal.Test - com.wuke.test.bigdecimal.Person object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0xf800ef95
12 4 int Person.i 0
16 8 long Person.j 0
24 4 com.wuke.test.bigdecimal.Student Person.student null
28 4 (object alignment gap)
Instance size: 32 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
===========调用对象的HashCode后==========
14:41:37.458 [main] INFO com.wuke.test.bigdecimal.Test - com.wuke.test.bigdecimal.Person object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000b68428601 (hash: 0x0b684286; age: 0)
8 4 (object header: class) 0xf800ef95
12 4 int Person.i 0
16 8 long Person.j 0
24 4 com.wuke.test.bigdecimal.Student Person.student null
28 4 (object alignment gap)
Instance size: 32 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
//执行结果分析:
1.`MarkWord`和`ClassPoint`分别占用了8个字节和4个字节,因为没有任何同步代码块使用该对象作为锁,
所以该对象处于一个无锁状态,即偏向锁的标志位为0,锁标志位01。
2.non-biasable表示没开启偏向锁,并且value的值是001结尾,对照上面的Mark Word状态表的后三位,刚好是无锁状态0,01
3.对象体此时有三个属性,分别是占用4个字节的int类型的i,占用8个字节的long类型变量j,和一个引用类型,长度为4。
4.因为JVM规定,对象头的大小必须是8字节的整数倍,因为 8 + 4 = 12,不够,所以需要额外的 4字节`object alignment gap(对齐字节)`进行填充。
5.对象初始化后,会自动调用`init`方法对对象属性进行赋值,int,long类型默认复制为0,引用类型默认赋值为null。
6.对象总大小为32字节。
7.从执行结果中可以看到当调用了对象的HashCode后,JVM会将其记录在对象头的Mark Word中。
注意:只有调用未重写的 Object.hashcode()
方法,或者调用 System.IdentityHashCode(obj)
方法时,其值才被记录到Mark Word
中;如果调用的是重写的hashcode()
方法,也不会记录到Mark Word
中。
创建数组示例
@Slf4j
public class Test {
public static void main(String[] args) {
Person[] persons = new Person[5];
log.info(ClassLayout.parseInstance(persons).toPrintable());
}
}
class Person {
private int i;
private long j;
private Student student;
}
class Student {
private int k;
}
//执行结果:
14:47:14.016 [main] INFO com.wuke.test.bigdecimal.Test - [Lcom.wuke.test.bigdecimal.Person; object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0xf800efd4
12 4 (array length) 5
12 4 (alignment/padding gap)
16 20 com.wuke.test.bigdecimal.Person Person;.<elements> N/A
36 4 (object alignment gap)
Instance size: 40 bytes
Space losses: 4 bytes internal + 4 bytes external = 8 bytes total
//执行结果分析:
1. 对象头中增加了一个数组长度的属性,这个属性在一般对象中是没有的,只有类型为数组类型才具备。
2. 对象体中只有一个属性,因为我们的数组申请的长度为4,一个引用的大小为4,所以该属性的大小为 4 * 5 = 20