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,我拉你進羣,一塊兒交流技術,一塊兒進階,一塊兒牛逼~~

相關文章
相關標籤/搜索