JOL分析内存

53 阅读4分钟

JOL简介

JOL(Java对象布局)是用于分析JVM中对象布局方案的微型工具箱。这些工具大量使用UnsafeJVMTI可服务性代理(SA)来解码实际的对象布局、示意图和引用。

JOL 的官方文档openjdk.java.net/projects/co…

JOL常用方法

//1.使用jol计算对象的大小(单位为字节): 
ClassLayout.parseInstance(obj).instanceSize();
//2.使用jol查看对象的内存布局:
ClassLayout.parseInstance(obj).toPrintable();

Mark Word的结构信息

image.png

JOL打印信息分析

OFF:偏移地址,单位字节。

SIZE:占用的内存大小,单位为字节。

TYPE DESCRIPTION: 类型描述。

(object header: mark)为对象头的Mark Word(标记字段)。

(object header: class)为对象头的Class Pointer(类型指针)。

(object alignment gap)因为在 64 位虚拟机上对象的大小必须是 8 的倍数)。 即:Mark WordSZ + Class PointerSZ + gapSZ = 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,锁标志位012.non-biasable表示没开启偏向锁,并且value的值是001结尾,对照上面的Mark Word状态表的后三位,刚好是无锁状态0,01

3.对象体此时有三个属性,分别是占用4个字节的int类型的i,占用8个字节的long类型变量j,和一个引用类型,长度为44.因为JVM规定,对象头的大小必须是8字节的整数倍,因为 8 + 4 = 12,不够,所以需要额外的 4字节`object alignment gap(对齐字节)`进行填充。

5.对象初始化后,会自动调用`init`方法对对象属性进行赋值,int,long类型默认复制为0,引用类型默认赋值为null6.对象总大小为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