Java 对象模型

HotSpot JVM 使用名为 oops (Ordinary Object Pointers) 的数据结构来表示对象。这些 oops 等同于本地 C 指针。 instanceOops 是一种特殊的 oop,表示 Java 中的对象实例。

在 Hotspot VM 中,对象在内存中的存储布局分为 3 块区域:

  • 对象头(Header)
  • 实例数据(Instance Data)
  • 对齐填充(Padding)

对象头又包括三部分:MarkWord、元数据指针、数组长度。

  • MarkWord:用于存储对象运行时的数据,好比 HashCode、锁状态标志、GC分代年龄等。这部分在 64 位操作系统下占 8 字节,32 位操作系统下占 4 字节。
  • 指针:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪一个类的实例。这部分就涉及到指针压缩的概念,在开启指针压缩(-XX:+UseCompressedClassPointers )的状况下占 4 字节,未开启状况下占 8 字节。
  • 数组长度:这部分只有是数组对象才有,若是是非数组对象就没这部分。这部分占 4 字节。

实例数据就不用说了,用于存储对象中的各类类型的字段信息(包括从父类继承来的)。

关于对齐填充,Java 对象的大小默认是按照 8 字节对齐,也就是说 Java 对象的大小必须是 8 字节的倍数。若是算到最后不够 8 字节的话,那么就会进行对齐填充。

invalid image(图片无法加载)

基础对象占用存储空间

Java 基础对象在内存中占用的空间如下:

类型 占用空间(byte)
boolean 1
byte 1
short 2
char 2
int 4
float 4
long 8
double 8

JOL(Java Object Layout)是用于分析 JVM 中对象布局方案的微型工具箱。这些工具大量使用 Unsafe、JVMTI 和 Serviceability Agent (SA) 来解码实际的 对象布局、占用空间和引用。这使得 JOL 比其他依赖堆转储、规范假设等的工具更准确。

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

Integer占用多少字节

由于现在基本都是 64 位的虚拟机,所以后面的讨论都是基于 64 位虚拟机。

ClassLayout classLayout = ClassLayout.parseInstance(4);
System.out.println(classLayout.toPrintable());

jvm参数:-XX:+UseCompressedClassPointers

执行结果

java.lang.Integer object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4        (object header: class)    0x00025938
 12   4    int Integer.value             4
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

jvm参数:-XX:-UseCompressedClassPointers

java.lang.Integer object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   8        (object header: class)    0x000001ff6316fd80
 16   4    int Integer.value             4
 20   4        (object alignment gap)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

一个空对象占用多少字节

publci class EmptyObject {
}

理论上一个空对象占用内存大小只有对象头信息,对象头占 8+4=12 个字节。那么 EmptyObject.class 应该占用的存储空间就是 12 字节,考虑到 8 字节的对齐填充,那么会补上 4 字节填充到 8 的 2倍,总共就是 16字节。怎么验证我们的结论呢?JDK 提供了一个工具,JOL 全称为 Java Object Layout,是分析 JVM 中对象布局的工具,该工具大量使用了 Unsafe、JVMTI 来解码布局情况。下面我们就使用这个工具来获取一个 Java 对象的大小。

public class HowManyBytesObjectOccupy {
    @Test
    public void testEmptyObject() {
        ClassLayout classLayout = ClassLayout.parseInstance(new EmptyObject());
        System.out.println(classLayout.toPrintable());
    }
}

class EmptyObject {
}

jvm参数:-XX:-UseCompressedClassPointers

javas.advanced.EmptyObject object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   8        (object header: class)    0x0000025eb9b60598
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

jvm参数:-XX:+UseCompressedClassPointers

javas.advanced.EmptyObject object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4        (object header: class)    0x0101a140
 12   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

测试其它

class FullObject {
    private byte b;
    private char type;
    private short age;
    private int no;
    private float weight;
    private double price;
    private long id;
    private boolean flag;
    private String str;
    private LocalDateTime dateTime;
    private String[] strs;
    private List<String> strList;
}


    @Test
    public void testFullObject() {
        ClassLayout classLayout = ClassLayout.parseInstance(new FullObject());
        System.out.println(classLayout.toPrintable());
    }

未指定jvm参数-XX:+UseCompressedClassPointers,但默认是开户压缩。

结果是

javas.advanced.FullObject object internals:
OFF  SZ                      TYPE DESCRIPTION               VALUE
  0   8                           (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4                           (object header: class)    0x0101a140
 12   4                       int FullObject.no             0
 16   8                    double FullObject.price          0.0
 24   8                      long FullObject.id             0
 32   4                     float FullObject.weight         0.0
 36   2                      char FullObject.type            
 38   2                     short FullObject.age            0
 40   1                      byte FullObject.b              0
 41   1                   boolean FullObject.flag           false
 42   2                           (alignment/padding gap)   
 44   4          java.lang.String FullObject.str            null
 48   4   java.time.LocalDateTime FullObject.dateTime       null
 52   4        java.lang.String[] FullObject.strs           null
 56   4            java.util.List FullObject.strList        null
 60   4                           (object alignment gap)    
Instance size: 64 bytes
Space losses: 2 bytes internal + 4 bytes external = 6 bytes total

注意有两处padding

  • 第一处是OFF(OFFSET)为42,原因是后面的字段是4byte,而此处缺少2byte,不能把后面字段的4byte拆开,所以要添加padding。
  • 第二处是OFF(OFFSET)为60。全部实例数据后需要padding。
作者:张三  创建时间:2025-03-05 18:54
最后编辑:张三  更新时间:2025-03-28 14:56