今天刷到了一个面试题:问创建长度为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());
输出
4. 总结
综上所述,一个长度为 10 的字符串实际占用空间应为 64,不考虑补齐的时候可以说是 60(保险起见面试的时候还是说清楚吧)。此外是 jdk1.9 及更高版本是 String 底层是 Byte 数组,应该重新计算,一般会更小。