最近一个网友问我,在面试阿里的时候,被问到了一个 String 字符串占用多少内存的问题?他当时懵了,因为他只记得基本数据类型占用的空间大小。
说实话,这个问题,如果是我,我也可以会回答错误。
网上曾经有一道非常著名的题,如下:
既然选项是字节,那我们就应该这么算。
String str = "搞java";
System.out.println("\"搞java\".length():" + (str.length()));
System.out.println("\"搞java\".getBytes().length:" + (str.getBytes().length));
System.out.println("\"搞java\".getBytes(\"GBK\").length:" + (str.getBytes("GBK").length));
System.out.println("\"搞java\".getBytes(\"UTF-8\").length:" + (str.getBytes("UTF-8").length));
/**
"搞java".length():5
"搞java".getBytes().length:6
"搞java".getBytes("GBK").length:6
"搞java".getBytes("UTF-8").length:7
*/
所以,选 A 或选 B 都说得过去。但是这是一个单选题,官方给出的最终正确答案是 A,即 6 个字节。原因是,我们的 Window 系统,默认的是 GBK,所以,答案为 A。
现在我们回归到的主题,一个 String 字符串占多少内存?就如上面这个选择题一样,看起来比 long 和 double 都要节约空间?
搜寻了好久,国外网友给出了一个计算公式:
这个公式看的我糊里糊涂的,我们看看 String 的源码。里面有一个 int 的 hash,还有一个 char [] 数组。所以,单纯的讲上面的字符串占用的空间肯定不止 6 个字节。
根据上面这个图,我们可以看出 String 对象的一个基本内存布局情况。需要注意的是 JDK 6 和 JDK 8 String 对象有所改动,计算方式不一样。
由上图可知,一个空 String 所占空间为:
对象头(8 字节)+ char 数组(16 字节)+ 3 个 int(3 × 4 = 12 字节)+1 个 char 数组的引用 (4 字节 ) = 40 字节。这里是 JDK 6 的计算方法。
JDK 8 中,只有一个 int 了,所以一个空的 String 占用 32 字节。
所以,我们最终可以总结一个公式:
// JDK 6
8*( ( 8+2*n+4+12)+7 ) / 8 = 8*(int) ( ( ( (n) *2 )+43) /8 )
// JDK 8
8*( ( 8+2*n+4+4)+7 ) / 8 = 8*(int) ( ( ( (n) *2 )+43) /8 )
其中 N 为字符串长度。
那个这个题,该怎么回答呢?说出你的思路,你对 String 内部结构的构成理解,和计算方式就可以了。实在记不住,也可以使用 Java 自带的 jol-core 工具来计算。
org.openjdk.jol
jol-core
0.9
引入上面的 pom 配置,然后编写简单的几行代码就可以计算出来。
public class XttblogObjectMemory {
public static void main(String[] args) {
System.out.print(ClassLayout.parseClass(String.class)
.toPrintable());
}
}
空字符串的内存占用情况,计算结果如下所示:
java.lang.String object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 char[] String.value N/A
16 4 int String.hash N/A
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
其他 Java 类的内存占用情况,也可以通过 jol-core 工具统计出来,用法和上面的类似。
以上,你知道的越多,不知道的就越多,业余的像一棵小草一样。