Java中的String到底占⽤多⼤的内存空间?你所了解的可能都是
错误的!!
写在前⾯
最近⼩伙伴加时,我总是问⼀个问题:Java中的String类占⽤多⼤的内存空间?很多⼩伙伴的回答着实让我哭笑不得,有说不占空间的,有说1个字节的,有说2个字节的,有说3个字节的,有说不知道的,更让⼈哭笑不得的是竟然还有⼈说是2的31次
⽅。那如果真是这样的话,服务器的内存空间还放不下⼀个字符串呀!作为程序员的我们,可不能闹这种笑话呀。今天,我们就⼀起来聊聊Java中的String到底占⽤多⼤的内存空间!
Java对象的结构
⾸先,我们来下Java对象在虚拟机中的结构,这⾥,以HotSpot虚拟机为例。
从上⾯的这张图⾥⾯可以看出,对象在内存中的结构主要包含以下⼏个部分:
Mark Word(标记字段):对象的Mark Word部分占4个字节,其内容是⼀系列的标记位,⽐如轻量级锁的标记位,偏向锁标记位等等。
Klass Pointer(Class对象指针):Class对象指针的⼤⼩也是4个字节,其指向的位置是对象对应的Class对象(其对应的元数据对象)的内存地址
对象实际数据:这⾥⾯包括了对象的所有成员变量,其⼤⼩由各个成员变量的⼤⼩决定,⽐如:byte和boolean是1个字节,short和char是2个字节,int和float是4个字节,long和double是8个字节,reference是4个字节
对齐:最后⼀部分是对齐填充的字节,按8个字节填充。
换种说法就是:
对象头(object header):8 个字节(保存对象的 class 信息、ID、在虚拟机中的状态)
Java 原始类型数据:如 int, float, char 等类型的数据
引⽤(reference):4 个字节
填充符(padding)
Java中的String类型
空String占⽤的空间
这⾥,我们以Java8为例进⾏说明。⾸先,我们来看看String类中的成员变量。
/** 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,如下所⽰。
kit.st;
import java.util.UUID;
/**
* @author binghe
* @version 1.0.0
* @description ⽣成没有-的UUID
*/
public class UUIDUtils {
public static String getUUID(){
String uuid = UUID.randomUUID().toString();
place("-", "");
}
}
接下来,创建⼀个TestString类,在main()⽅法中创建⼀个长度为4000000的数组,然后在数组中放满UUID字符串,如下所⽰。
kit.st;
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] = UUID();
System.out.println(i);
}
//防⽌程序退出
while(true){
}
}
}
字符串常量在内存中占的字节数这⾥,4000000个字符串,每个字符串的长度为32,所以保存字符串数据所占⽤的内存空间为:(40 + 32 * 2) * 4000000 = 416000000字节,约等于416MB。
我们使⽤Jprofiler内存分析⼯具进⾏分析:
可以看到,使⽤Jprofiler内存分析⼯具的结果为:321MB + 96632KB,约等于417MB。之所以使⽤Jprofiler内存分析⼯具得出的结果⽐我们计算的⼤些,是因为在程序实际运⾏的过程中,程序内部也会⽣成⼀些字符串,这些字符串也会占⽤内存空间!!
所以,使⽤Jprofiler内存分析⼯具得出的结果符合我们的预期。
好了,今天就到这⼉吧,希望⼩伙伴们能有所收获,我是冰河,我们下期见!!
重磅福利
搜⼀搜【冰河技术】,关注这个有深度的程序员,每天阅读超硬核技术⼲货,内回复【PDF】有我准备的⼀线⼤⼚⾯试资料和我原创的超硬核PDF技术⽂档,以及我为⼤家精⼼准备的多套简历模板(不断更新中),希望⼤家都能到⼼仪的⼯作,学习是⼀条时⽽郁郁寡欢,时⽽开怀⼤笑的路,加油。如果你通过努⼒成功进⼊到了⼼仪的公司,⼀定不要懈怠放松,职场成长和新技术学习⼀样,不进则退。如果有幸我们江湖再见!
另外,我开源的各个PDF,后续我都会持续更新和维护,感谢⼤家长期以来对冰河的⽀持!!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。