Java中String到底占用多大的内存空间?

写在前面

对于Java中的String类占用多大的内存空间这个问题,是最近面试中问的比较多的一个问题。不少小伙伴的回答的都不是很正确,有说不占空间的,有说1个字节的,有说2个字节的,有说3个字节的,有说不知道的,更让人啼笑皆非的是居然还有人说是2的31次方。那若是真是这样的话,服务器的内存空间还放不下一个字符串呀!做为程序员的咱们,可不能闹这种笑话呀。今天,咱们就一块儿来聊聊Java中的String到底占用多大的内存空间!java

Java对象的结构

首先,咱们来下Java对象在虚拟机中的结构,这里,以HotSpot虚拟机为例。程序员

<p align="right">注:图片来源http://r6d.cn/wp7q</p>面试

从上面的这张图里面能够看出,对象在内存中的结构主要包含如下几个部分:数组

  • 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 字节 。dom

因此,咱们能够得出一个空String对象所占用的内存空间,以下所示。工具

对象头(8 字节)+ 引用 (4 字节 )  + char 数组(16 字节)+ 1个 int(4字节)+ 1个long(8字节)= 40 字节

因此,小伙伴们,大家的回答正确吗?测试

非空String占用的空间

若是String字符串的长度大于0的话,咱们也能够得出String占用内存的计算公式,以下所示。ui

40 + 2 * n

其中,n为字符串的长度。

这里,可能有小伙伴会问,为何是 40 + 2 n 呢?这是由于40是空字符串占用的内存空间,这个咱们上面已经说过了,String类其实是把数据存储到char[]这个成员变量数组中的,而char[]数组中的一个char类型的数据占用2个字节的空间,因此,只是String中的数据就会占用 2 n(n为字符串的长度)个字节的空间,再加上空字符串所占用的40个字节空间,最终得出一个字符串所占用的存储空间为: 40 + 2 * n (n为字符串长度)。

所以在代码中大量使用String对象时,应考虑内存的实际占用状况。

注:40 + 2 * n 这个公式咱们能够当作是计算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内存分析工具得出的结果符合咱们的预期。

好了,今天就到这儿吧,我是冰河,你们有啥问题能够在下方留言,也能够加我微信:sun_shine_lyz,我拉你进群,一块儿交流技术,一块儿进阶,一块儿牛逼~~

相关文章
相关标签/搜索