Contents

JVM基础:对象布局实战:用 JOL 工具看看真实结构

纸上得来终觉浅,我们现在来用工具实战验证一下:Java 对象在内存中到底长什么样?

JDK 提供了一款由官方支持的内存分析利器 —— JOL(Java Object Layout),它能让我们像 X 光一样看穿对象的每一个字节。


1. 什么是 JOL?

JOL 全称是 Java Object Layout,是 OpenJDK 官方提供的工具库,用于分析对象在内存中的布局。

GitHub 项目地址: https://github.com/openjdk/jol

Maven 依赖如下:

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

2. 实战演示:一个简单类的内存结构

我们先来看这个类:

public class SimpleObject {
    private int id;
    private boolean flag;
}

使用 JOL 工具查看内存结构:

import org.openjdk.jol.info.ClassLayout;

public class JOLTest {

    public static void main(String[] args) {

        SimpleObject simpleObject = new SimpleObject();
        System.out.println(ClassLayout.parseInstance(simpleObject).toPrintable());
    }

}

输出如下(以 64 位 JVM、开启压缩指针为例):

OFF  SZ      TYPE DESCRIPTION               VALUE
  0   8           (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4           (object header: class)    0xf800c143
 12   4       int SimpleObject.id           0
 16   1   boolean SimpleObject.flag         false
 17   7           (object alignment gap)    
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

解读说明:

  • 对象头:8 字节(Mark Word)+ 4 字节(Klass Pointer)= 12 字节
  • 字段部分:int 占 4 字节,boolean 占 1 字节
  • Padding:为了对齐成 8 的倍数,填充了 7 字节
  • 总大小:17 字节(但真实占用内存空间是 24 字节)

3. 换个顺序会怎样?字段重排实验

将字段顺序改为:

private boolean flag;
private int id;

再次执行 JOL,输出结果:

OFF  SZ      TYPE DESCRIPTION               VALUE
  0   8           (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4           (object header: class)    0xf800c143
 12   4       int SimpleObject.id           0
 16   1   boolean SimpleObject.flag         false
 17   7           (object alignment gap)    
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

内存中字段并没有像定义的那样排列。


4. 对象的继承结构如何布局?

继承关系的类,会将父类字段排在前面,子类字段依次跟在后面。例如:

class A {
    short a;
}

class B extends A {
    boolean b;
    long c;
}

JOL 分析后,你会看到:

  • short a 是来自父类的字段,排在前面。
  • boolean blong c 属于子类,跟在后面。
  • 同样存在字节对齐与填充问题。
OFF  SZ      TYPE DESCRIPTION               VALUE
  0   8           (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4           (object header: class)    0xf800cde1
 12   2     short A.a                       0
 14   2           (alignment/padding gap)   
 16   8      long B.c                       0
 24   1   boolean B.b                       false
 25   7           (object alignment gap)    
Instance size: 32 bytes
Space losses: 2 bytes internal + 7 bytes external = 9 bytes total

5. JVM的字段重排规则

  • 规则 1:每个对象都按 8 字节的粒度对齐。
  • 规则 2:类属性的排序如下:首先是long和double;然后是int和float;然后是char和short;然后是byte和boolean,最后是引用。属性按其自身的粒度对齐。
  • 规则 3:属于层次结构中不同类的字段绝不能混合在一起。父类的字段优先排列,遵循规则2,然后是子类的字段。
  • 规则 4:在父类的最后一个字段和子类的第一个字段之间必须有填充以对齐到 4 字节边界。
  • 规则 5:当子类的第一个字段是长整型或双精度型,且父类未按 8 字节边界对齐时,JVM 将违反规则2,并尝试依次在为子类保留的空间的开头放置 int、短整型、字节型和引用,直到填满空隙。

✅ 小结

  • JOL 能让你可视化对象的真实内存结构,适用于调优。
  • 字段顺序不当,会引入大量的内存碎片(padding)。
  • 对齐规则不是摆设,真实项目中高频对象的内存效率会被严重影响。