乐趣区

关于java:Java中String到底占用多大的内存空间

写在后面

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

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

所以,咱们能够得出一个空 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 为字符串长度)。

因而在代码中大量应用 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,我拉你进群,一起交换技术,一起进阶,一起牛逼~~

退出移动版