Java中的String到底佔用多大的內存空間?你所瞭解的可能都是錯誤的!!

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"寫在前面"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最近小夥伴加羣時,我總是問一個問題:Java中的String類佔用多大的內存空間?很多小夥伴的回答着實讓我哭笑不得,有說不佔空間的,有說1個字節的,有說2個字節的,有說3個字節的,有說不知道的,更讓人哭笑不得的是竟然還有人說是2的31次方。那如果真是這樣的話,服務器的內存空間還放不下一個字符串呀!作爲程序員的我們,可不能鬧這種笑話呀。今天,我們就一起來聊聊Java中的String到底佔用多大的內存空間!"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Java對象的結構"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,我們來下Java對象在虛擬機中的結構,這裏,以HotSpot虛擬機爲例。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8c/8c539363612ade7cf8830e0fb8228a2f.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注:圖片來源http://r6d.cn/wp7q"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從上面的這張圖裏面可以看出,對象在內存中的結構主要包含以下幾個部分:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Mark Word(標記字段):對象的Mark Word部分佔4個字節,其內容是一系列的標記位,比如輕量級鎖的標記位,偏向鎖標記位等等。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Klass Pointer(Class對象指針):Class對象指針的大小也是4個字節,其指向的位置是對象對應的Class對象(其對應的元數據對象)的內存地址"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對象實際數據:這裏麪包括了對象的所有成員變量,其大小由各個成員變量的大小決定,比如:byte和boolean是1個字節,short和char是2個字節,int和float是4個字節,long和double是8個字節,reference是4個字節"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對齊:最後一部分是對齊填充的字節,按8個字節填充。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"換種說法就是:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對象頭(object header):8 個字節(保存對象的 class 信息、ID、在虛擬機中的狀態)"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java 原始類型數據:如 int, float, char 等類型的數據"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"引用(reference):4 個字節"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"填充符(padding)"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Java中的String類型"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"空String佔用的空間"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏,我們以Java8爲例進行說明。首先,我們來看看String類中的成員變量。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/** The value is used for character storage. */\nprivate final char value[];\n \n/** Cache the hash code for the string */\nprivate int hash; // Default to 0\n \n/** use serialVersionUID from JDK 1.0.2 for interoperability */\nprivate static final long serialVersionUID = -6849794470754667710L;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Java 裏數組也是對象,因此數組也有對象頭。所以,一個數組所佔的空間爲對象頭所佔的空間加上數組長度加上數組的引用,即 8 + 4 + 4= 16 字節 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,我們可以得出一個空String對象所佔用的內存空間,如下所示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"對象頭(8 字節)+ 引用 (4 字節 ) + char 數組(16 字節)+ 1個 int(4字節)+ 1個long(8字節)= 40 字節"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"所以,小夥伴們,你們的回答正確嗎?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"非空String佔用的空間"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果String字符串的長度大於0的話,我們也可以得出String佔用內存的計算公式,如下所示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"40 + 2 * n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中,n爲字符串的長度。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏,可能有小夥伴會問,爲什麼是 40 + 2 "},{"type":"text","marks":[{"type":"italic"}],"text":" n 呢?這是因爲40是空字符串佔用的內存空間,這個我們上面已經說過了,String類實際上是把數據存儲到char[]這個成員變量數組中的,而char[]數組中的一個char類型的數據佔用2個字節的空間,所以,只是String中的數據就會佔用 2 "},{"type":"text","text":" n(n爲字符串的長度)個字節的空間,再加上空字符串所佔用的40個字節空間,最終得出一個字符串所佔用的存儲空間爲: 40 + 2 * n (n爲字符串長度)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注:40 + 2 * n 這個公式我們可以看成是計算String對象佔用多大內存空間的通用公式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此在代碼中大量使用String對象時,應考慮內存的實際佔用情況。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"驗證結論"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來,我們就一起來驗證下我們上面的結論。首先,創建一個UUIDUtils類用來生成32位的UUID,如下所示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"package io.mykit.binghe.string.test;\n\nimport java.util.UUID;\n\n/**\n * @author binghe\n * @version 1.0.0\n * @description 生成沒有-的UUID\n */\npublic class UUIDUtils {\n\tpublic static String getUUID(){\n\t\tString uuid = UUID.randomUUID().toString();\n\t\treturn uuid.replace(\"-\", \"\");\n\t}\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來,創建一個TestString類,在main()方法中創建一個長度爲4000000的數組,然後在數組中放滿UUID字符串,如下所示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"package io.mykit.binghe.string.test;\n\nimport java.util.UUID;\n\n/**\n * @author binghe\n * @version 1.0.0\n * @description 測試String佔用的內存空間\n */\npublic class TestString{\n public static void main(String[] args){\n String[] strContainer = new String[4000000];\n for(int i = 0; i < 4000000; i++){\n strContainer[i] = UUIDUtils.getUUID();\n System.out.println(i);\n }\n //防止程序退出\n while(true){\n\n }\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏,4000000個字符串,每個字符串的長度爲32,所以保存字符串數據所佔用的內存空間爲:(40 + 32 "},{"type":"text","marks":[{"type":"italic"}],"text":" 2) "},{"type":"text","text":" 4000000 = 416000000字節,約等於416MB。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們使用Jprofiler內存分析工具進行分析:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b5/b5eaffd7be02486915204a9a1f9f2193.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看到,使用Jprofiler內存分析工具的結果爲:321MB + 96632KB,約等於417MB。之所以使用Jprofiler內存分析工具得出的結果比我們計算的大些,是因爲在程序實際運行的過程中,程序內部也會生成一些字符串,這些字符串也會佔用內存空間!!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,使用Jprofiler內存分析工具得出的結果符合我們的預期。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"好了,今天就到這兒吧,希望小夥伴們能有所收穫,我是冰河,我們下期見!!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"重磅福利"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微信搜一搜【冰河技術】微信公衆號,關注這個有深度的程序員,每天閱讀超硬核技術乾貨,公衆號內回覆【PDF】有我準備的一線大廠面試資料和我原創的超硬核PDF技術文檔,以及我爲大家精心準備的多套簡歷模板(不斷更新中),希望大家都能找到心儀的工作,學習是一條時而鬱鬱寡歡,時而開懷大笑的路,加油。如果你通過努力成功進入到了心儀的公司,一定不要懈怠放鬆,職場成長和新技術學習一樣,不進則退。如果有幸我們江湖再見! "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外,我開源的各個PDF,後續我都會持續更新和維護,感謝大家長期以來對冰河的支持!!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"寫在最後"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你覺得冰河寫的還不錯,請微信搜索並關注「 "},{"type":"text","marks":[{"type":"strong"}],"text":"冰河技術"},{"type":"text","text":" 」微信公衆號,跟冰河學習高併發、分佈式、微服務、大數據、互聯網和雲原生技術,「 "},{"type":"text","marks":[{"type":"strong"}],"text":"冰河技術"},{"type":"text","text":" 」微信公衆號更新了大量技術專題,每一篇技術文章乾貨滿滿!不少讀者已經通過閱讀「 "},{"type":"text","marks":[{"type":"strong"}],"text":"冰河技術"},{"type":"text","text":" 」微信公衆號文章,吊打面試官,成功跳槽到大廠;也有不少讀者實現了技術上的飛躍,成爲公司的技術骨幹!如果你也想像他們一樣提升自己的能力,實現技術能力的飛躍,進大廠,升職加薪,那就關注「 "},{"type":"text","marks":[{"type":"strong"}],"text":"冰河技術"},{"type":"text","text":" 」微信公衆號吧,每天更新超硬核技術乾貨,讓你對如何提升技術能力不再迷茫!"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/86/86642f445ccf193620f77650ac1ae2bc.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章