我目前正在嘗試處理一個包含字串行的大型 txt 檔案(略小于 2GB)。我正在將其所有內容從 a 加載InputStream到 a List<String>。我通過以下代碼段做到這一點:
try(BufferedReader reader = new BufferedReader(new InputStreamReader(zipInputStream))) {
List<String> data = reader.lines()
.collect(Collectors.toList());
}
問題是,檔案 itsef 小于 2GB,但是當我查看記憶體時,JVM 分配的檔案大小是檔案的兩倍:

此外,這里是記憶體中最重的物件:

所以我的理解是,Java 分配了兩次操作所需的記憶體,一次將檔案內容放入位元組陣列中,另一次用于實體化字串串列。
我的問題是:我們可以優化它嗎?避免需要兩倍的記憶體大小?
uj5u.com熱心網友回復:
tl; dr String物件每個字符可以占用 2 個位元組。
長答案:概念上 aString是 的序列char。每個char將代表一個代碼點(或一個代碼點的一半,但我們現在可以忽略該細節)。
每個代碼點往往代表一個字符(有時多個代碼點構成一個“字符”,但這是我們可以忽略此答案的另一個細節)。
這意味著,如果您讀取以單位元組編碼(通常是 ISO-8859-* 系列的成員)或可變位元組編碼(主要是 UTF-8)存盤的 2 GB 文本檔案,那么記憶體中的大小在 Java 中很容易成為磁盤大小的兩倍。
String 現在對此有很多警告,主要是Java可以(作為內部的,不可見的操作)在當且僅當使用的字符允許時(如果它們適合固定的內部編碼,則有效)為每個字符使用單個位元組JVM為此選擇了)。但這似乎并沒有發生在你身上。
你能做些什么來避免這種情況?這取決于您的用例是什么:
- 首先不要
String用于存盤資料。奇怪的是,這些資料實際上代表了某種結構,如果將其決議為專用格式,則可能會減少記憶體使用量。 - 不要將所有內容都保存在記憶體中:通常情況下,您實際上并不需要一次將所有內容都保存在記憶體中。而是在讀取資料時處理并寫掉資料,因此記憶體中的記錄永遠不會超過一手。
- 為您的特定用例構建您自己的類似字串的資料型別。雖然構建一個完整的字串替換是一項艱巨的任務,但如果您知道您需要哪些功能子集,它實際上可能是一個非常可克服的挑戰。
- 嘗試確保資料存盤為緊湊的字串,如果可能的話,通過弄清楚為什么還沒有發生(這需要深入挖掘你的 JVM 的細節)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/527240.html
