/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
在 Java 里数组也是对象,因此数组也有对象头。所以,一个数组所占的空间为对象头所占的空间加上数组长度加上数组的引用,即 8 + 4 + 4= 16 字节 。
所以,我们可以得出一个空String对象所占用的内存空间,如下所示。
对象头(8 字节)+ 引用 (4 字节 ) + char 数组(16 字节)+ 1个 int(4字节)+ 1个long(8字节)= 40 字节
所以,小伙伴们,你们的回答正确吗?
非空String占用的空间
如果String字符串的长度大于0的话,我们也可以得出String占用内存的计算公式,如下所示。
40 + 2 * n
其中,n为字符串的长度。
这里,可能有小伙伴会问,为什么是 40 + 2 * n 呢?这是因为40是空字符串占用的内存空间,这个我们上面已经说过了,String类实际上是把数据存储到char[]这个成员变量数组中的,而char[]数组中的一个char类型的数据占用2个字节的空间,所以,只是String中的数据就会占用 2 * n(n为字符串的长度)个字节的空间,再加上空字符串所占用的40个字节空间,最终得出一个字符串所占用的存储空间为: 40 + 2 * n (n为字符串长度)。
注:40 + 2 * n 这个公式我们可以看成是计算String对象占用多大内存空间的通用公式。
因此在代码中大量使用String对象时,应考虑内存的实际占用情况。
接下来,我们就一起来验证下我们上面的结论。首先,创建一个UUIDUtils类用来生成32位的UUID,如下所示。
package io.mykit.binghe.string.test;
import java.util.UUID;
/**
-
@author binghe
-
@version 1.0.0
-
@description 生成没有-的UUID
*/
public class UUIDUtils {
public static String getUUID(){
String uuid = UUID.randomUUID().toString();
return uuid.replace(“-”, “”);
}
}
接下来,创建一个TestString类,在main()方法中创建一个长度为4000000的数组,然后在数组中放满UUID字符串,如下所示。
package io.mykit.binghe.string.test;
import java.util.UUID;
/**
-
@author binghe
-
@version 1.0.0
-
@description 测试String占用的内存空间
*/
public class TestString{
public static void main(String[] args){
String[] strContainer = new String[4000000];
for(int i = 0; i < 4000000; i++){
strContainer[i] = UUIDUtils.getUUID();
System.out.println(i);
}
//防止程序退出
while(true){
}
}
}
这里,4000000个字符串,每个字符串的长度为32,所以保存字符串数据所占用的内存空间为:(40 + 32 * 2) * 4000000 = 416000000字节,约等于416MB。
我们使用Jprofiler内存分析工具进行分析:
可以看到,使用Jprofiler内存分析工具的结果为:321MB + 96632KB,约等于417MB。之所以使用Jprofiler内存分析工具得出的结果比我们计算的大些,是因为在程序实际运行的过
程中,程序内部也会生成一些字符串,这些字符串也会占用内存空间!!
所以,使用Jprofiler内存分析工具得出的结果符合我们的预期。
好了,今天就到这儿吧,希望小伙伴们能有所收获,我是冰河,我们下期见!!