硬核子牙公众号原文:你知道如何计算Java的对象大小吗
本文基于子牙老师的讲解,用来记录本人的理解。
一、理论篇
本文计算的是Java对象在 堆区 的大小。学习和掌握该知识,有助于分析“应用系统的堆到底该设置为多大?”的问题。
接着,我整理了一张思维导图,来对对象结构中的各部分大小进行解释:
关于实例数据区域数据大小如下表所示:
数据类型 | 数据长度 |
---|---|
boolean | 1B |
byte | 1B |
char | 2B |
int | 4B |
float | 4B |
long | 8B |
double | 8B |
引用类型 | (开启指针压缩)4B (关闭指针压缩)8B |
二、猜想篇
结合第一章《理论篇》的知识,猜想一下,在64位虚拟机下,Java对象的大小。
目前,主流的服务器基本上都使用的是64位Java虚拟机,因此,接下来计算Java对象大小时,都以此为前提。(不考虑32位虚拟机的情况)
2.1 Object对象大小
Object obj = new Object();
开启指针压缩 | 关闭指针压缩 | |
---|---|---|
Mark Word | 8B | 8B |
Klass Pointer | 4B | 8B |
实例数据 | 0B | 0B |
对齐填充 | 4B | 0B |
合计 | 16B | 16B |
在64位Java虚拟机下,Mark Word 都为 8B;
开启指针压缩时,因为 8B + 4B + 0B + 0B = 12B,不符合 JVM 所有对象按 8 字节对齐的规则。因此,对象大小扩展到 16B,就可以被 8 整除了。此时,对齐填充的大小等于 4B。
2.2 包含实例数据的对象
public class ObjectWithInstanceData {
private int a = 10;
private double b = 10.0d;
}
开启指针压缩 | 关闭指针压缩 | |
---|---|---|
Mark Word | 8B | 8B |
Klass Pointer | 4B | 8B |
实例数据 | 12B | 12B |
对齐填充 | 0B | 4B |
合计 | 24B | 32B |
2.3 数组对象
int[] array = {1,2,3};
开启指针压缩 | 关闭指针压缩 | |
---|---|---|
Mark Word | 8B | 8B |
Klass Pointer | 4B | 8B |
数组长度 | 4B | 4B |
对齐填充(数组对象才有的) | 0B | 4B |
实例数据 | 12B | 12B |
对齐填充 | 4B | 4B |
合计 | 32B | 40B |
三、验证篇
3.1 指针压缩的 JVM 参数
指针压缩的 JVM 参数:
# 开启指针压缩(JVM默认开启的)
-XX:+UseCompressedOops
# 关闭指针压缩
-XX:-UseCompressedOops
3.2 借助 jol-core
这是在代码中打印对象大小的方法。在 Maven 项目的 pom.xml 中引入依赖:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
打印对象大小的 API 是
String value = ClassLayout.parseInstance(obj).toPrintable();
System.out.println(value);
我索性一口气都打印出来:
import org.openjdk.jol.info.ClassLayout;
public class ObjectWithInstanceData {
private int a = 10;
private double b = 10.0d;
public static void main(String[] args) {
Object obj = new Object();
String value = ClassLayout.parseInstance(obj).toPrintable();
System.out.println(value);
obj = new ObjectWithInstanceData();
value = ClassLayout.parseInstance(obj).toPrintable();
System.out.println(value);
obj = new int[]{1,2,3};
value = ClassLayout.parseInstance(obj).toPrintable();
System.out.println(value);
}
}
开启指针压缩的 Object 对象结构:
开启指针压缩的 ObjectWithInstanceData 对象结构:
开启指针压缩的 int 数组对象结构:
(array length) 和 (alignment/padding gap) 的 OFFSET 和 SZ 相同,占据同一块内存,因此也可以说是对齐字节为 0
关闭指针压缩的 Object 对象结构:
关闭指针压缩的 ObjectWithInstanceData 对象结构:
关闭指针压缩的 int 数组对象结构:
(array length) 和 (alignment/padding gap) 的 OFFSET 相同,从内存相同位置开始,(alignment/padding gap) SZ 比 (array length) SZ 多 4 个字节,因此也可以说是对填充齐字节为 4。