JVM对象内存布局如下图所示:
包含以下关键部分:
-
对象头:
-
标记字(Mark Word):这部分数据包含对象自身的运行时数据,如哈希码(HashCode)、垃圾回收信息(如年龄段、是否被锁定等)、偏向线程ID、锁状态标志等。其长度通常是一个机器字(word),在32位JVM上是4字节,在64位JVM上是8字节。对象处于不同的状态,Mark Work会有不同的取值。
32位对象头如下图所示:
-
类型指针(Klass Pointer):这是指向它的类元数据的指针,JVM通过这个指针来确定对象属于哪个类。默认8个字节,启了压缩指针的情况下,这个指针会被压缩成4个字节大小。
启用压缩指针:-XX:+UseCompressedOops
可以减少类型指针的大小,从而减少对象总体大小,提高内存利用率。
-
数组长度:这是数组对象特有的部分,存储数组的长度。数组长度的存在对于数组操作非常关键,因为它需要在运行时进行边界检查以防止越界访问。
-
-
实例数据:这部分是对象存储其实际信息的地方,即从类中声明的各个字段。字段的排列顺序和数据类型大小可以影响对象的总大小,因为JVM会尝试以最有效的方式来存储这些字段:
- 字段排序:通常,为了内存对齐,JVM可能会根据字段的类型重新排序(如首先是
longs
和doubles
,然后是ints
,之后是shorts
和chars
,最后是bytes
和booleans
),这种排序有时候可以在不增加对象大小的情况下,最大限度地减少内部空隙。 - 继承结构:在Java中,对象实例数据也包括从其所有父类继承的字段。
- 字段排序:通常,为了内存对齐,JVM可能会根据字段的类型重新排序(如首先是
-
对齐填充:为了使对象的总大小是8字节(在大多数现代计算机上是内存访问的最佳边界)的倍数,可能需要在对象最后添加一些填充。这种对齐可以帮助提高CPU的缓存利用率,从而提高性能。
在64位系统下,开启了压缩指针,并且压缩指针有效的情况下,对象头占用12个字节。