Skip to content

本篇会从JOL工具的使用开始引入,先看看一个空的Object对象在JVM中占多少内存,再分析Object对象以什么格式在内存中存储。

一个Object对象在JVM中占多少内存?

想要知道一个java对象占用的内存大小,一般用一个JOL工具来计算,它是OpenJDK官方提供的java对象内存查看工具,先加入如下依赖:

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

测试代码如下,其中MyObject是个空对象public class MyObject {}

java
public static void main(String[] args) {
    // 64位,对象分布 未启动指针压缩
    Layouter l = new HotSpotLayouter(new X86_64_DataModel());
    System.out.println(ClassLayout.parseInstance(new MyObject(), l).toPrintable());
    System.out.println("======================================");

    // 64位,对象分布 启动指针压缩
    l = new HotSpotLayouter(new X86_64_COOPS_DataModel());
    System.out.println(ClassLayout.parseInstance(new MyObject(), l).toPrintable());
}

输出结果如下:

java
com.xk857.test3.MyObject object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           11 00 00 00 (00010001 00000000 00000000 00000000) (17)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           92 c2 00 20 (10010010 11000010 00000000 00100000) (536920722)
     12     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

======================================
com.xk857.test3.MyObject 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)                           92 c2 00 20 (10010010 11000010 00000000 00100000) (536920722)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

无论启用还是未启用指针压缩,空对象的大小都是16个字节,其中

  • 未启动指针压缩:8字节的markwork 8字节的klass指针
  • 未启动指针压缩:8字节的markwork 4字节的klass指针 以及 4字节对其填充

Object对象以什么格式在内存中存储

通过上文JOL工具可以得出下图结论

Object在内存中如何存储

  • markwork:用于存储对象自身的运行时数据,如哈希码(HashCode) 、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间 戳等等。MarkWork在32位JVM中的长度是32bit, 在64位JVM中长度是64bit。(关于markwork后文会有详细介绍)
  • klass指针:一个class文件被JVM加载之后,就会被解析成一个klass对象存储在方法区中。
  • 对齐填充:它存在的目的是为了保持对象的大小与8字节的倍数对齐。

什么是压缩指针?

上文有提到过压缩指针,压缩指针指的是类型指针记录的是该对象类型在MetaSpace的地址引用,指向方法区中Class信息的指针,意味着该对象可随时知道自己是哪个Class的实例。

比如new JavaObject0这个对象, 类型指针记录的就是JavaObject.class的地址引用;类型指针占用的内存大小分两种情况,当开启对象压缩时占用4字节UVM默认开启),关闭时占用8字节

压缩指针不仅可以作用于对象头的类型指针,还可以作用于引用类型的字段,以及引用类型的数组。在64位操作系统中,对象头中的类型指针占用64位(8字节),开启压缩指针后占用32位(4字节),压缩指针的目的即节省内存空间。

为什么要进行8字节的内存对齐

由于CPU进行内存访问时,一次寻址的指针大小是8字节,正好也是L1缓存行的大小;如果不进行内存对齐,则可能出现跨缓存行的情况,这叫做缓存行污染。

之所以叫做“污染”,是由于当obj1对象的字段被修改后,那么CPU在访问obj2对象时,必须将其重新加载到缓存行,因此影响了程序执行效率。