Java中的String对象大小应该怎么算

1,168 阅读2分钟

今天刷到了一个面试题:问创建长度为10的字符串占用多少存储空间?

网上的说法很简单:40 + 2n = 60 字节。

但是在我自己实验的过程中好像有些异样,就此来理清这个问题。

1. String对象

HotSpot虚拟机的对象内存布局可分为三部分。

1.1 对象头

在64位虚拟机上,String对象的MarkWord为 8 Byte,启了指针压缩,因此 Klass Pointer 大小为 4 Byte。 共计 12 Byte。(网上很多8 Byte 的说法是不对的)

1.2 实例数据

实例数据有两个

/** The value is used for character storage. */
private final char value[];

/** Cache the hash code for the string */
private int hash; // Default to 0

char[] 组为引用数据类型,大小为 4 Byte。 int 为基本数据类型,大小为 4 Byte。 共计 8 Byte。

1.3 对齐填充

当前占用空间 20 Byte。HotSpot VM 自动内存管理系统要求对象大小必须是 8 字节的整数倍,前两者之和不是 8 的倍数时靠此部分补齐。因此需要补到 24 Byte,这部分大小为 4 Byte。

综上所述 64 位虚拟机开启指针压缩的情况下 String 大小总为 24 Byte。 那么答案对不上啊!答案是还应该把其中的char数组算进取。

2. char数组对象

数组对象的对象头大小要比其他对象大 4 Byte,用来存储数组长度,因此对象头为 16 Byte。 数组长度为 0 时,实例数据长度为 0 。 另外对象长度刚好不用对齐。

3. 字符串占用空间

这么来看字符串实际占用空间应包含两个对象的占用,String 对象和 char[] 对象大小之和 为 40 Byte。 那么长度为 10 的字符串应该是 40 + 20 补至 8 的倍数为 64 Byte。

使用jdk的ObjectSizeCalculator验证

String s = "HelloWorld";
System.out.println("size of \"HelloWorld\": " + ObjectSizeCalculator.getObjectSize(s));

输出

size of "HelloWorld": 64

使用JOL验证

System.out.println(ClassLayout.parseInstance(s).toPrintable());
System.out.println(ClassLayout.parseInstance(s.toCharArray()).toPrintable());

输出

QQ截图20220812141933.png

4. 总结

综上所述,一个长度为 10 的字符串实际占用空间应为 64,不考虑补齐的时候可以说是 60(保险起见面试的时候还是说清楚吧)。此外是 jdk1.9 及更高版本是 String 底层是 Byte 数组,应该重新计算,一般会更小。