[英]Reading large file in Java — Java heap space
我正在讀取一個大的 tsv 文件(~40G)並嘗試通過逐行讀取來修剪它並僅將某些行打印到新文件中。 但是,我不斷收到以下異常:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2894)
at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:117)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:532)
at java.lang.StringBuffer.append(StringBuffer.java:323)
at java.io.BufferedReader.readLine(BufferedReader.java:362)
at java.io.BufferedReader.readLine(BufferedReader.java:379)
下面是代碼的主要部分。 我將緩沖區大小指定為 8192 以防萬一。 一旦達到緩沖區大小限制,Java 是否不會清除緩沖區? 我看不出是什么原因導致 memory 使用量很大。 我試圖增加堆大小,但沒有任何區別(具有 4GB RAM 的機器)。 我還嘗試每隔 X 行刷新 output 文件,但它也沒有幫助。 我在想也許我需要打電話給 GC,但聽起來不對。
有什么想法嗎? 非常感謝。 順便說一句 - 我知道我應該只調用一次 trim() ,存儲它,然后使用它。
Set<String> set = new HashSet<String>();
set.add("A-B");
...
...
static public void main(String[] args) throws Exception
{
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile),"UTF-8"), 8192);
PrintStream output = new PrintStream(outputFile, "UTF-8");
String line = reader.readLine();
while(line!=null){
String[] fields = line.split("\t");
if( set.contains(fields[0].trim()+"-"+fields[1].trim()) )
output.println((fields[0].trim()+"-"+fields[1].trim()));
line = reader.readLine();
}
output.close();
}
最有可能的是,文件沒有行終止符,因此閱讀器只是不斷增長它的 StringBuffer 無界,直到它用完 memory。
解決方案是一次讀取固定數量的字節,使用讀取器的“讀取”方法,然后在較小的緩沖區中查找新行(或其他解析標記)。
您確定文件中的“行”由換行符分隔嗎?
我有3個理論:
輸入文件不是 UTF-8 而是一些不確定的二進制格式,當讀取為 UTF-8 時會導致非常長的行。
該文件包含一些非常長的“行”......或者根本沒有換行符。
您沒有向我們展示的代碼中發生了其他事情; 例如,您正在向set
添加新元素。
為了幫助診斷:
od
之類的工具(在 UNIX / LINUX 上)確認輸入文件確實包含有效的行終止符; 即 CR、NL 或 CR NL。 作為記錄,您對trim
的略微次優使用與此問題無關。
一種可能性是您在垃圾收集期間用完了堆空間。 Hotspot JVM 默認使用並行收集器,這意味着您的應用程序分配對象的速度可能快於收集器回收對象的速度。 通過快速分配和丟棄,我已經能夠導致 OutOfMemoryError 據稱只有 10K 活動(小)對象。
您可以嘗試使用帶有選項的舊(1.5 之前)串行收集器-XX:+UseSerialGC
。 您可以使用其他幾個“擴展” 選項來調整集合。
您可能想嘗試從循環中刪除String[] fields
聲明。 當您在每個循環中創建一個新數組時。 你可以重復使用舊的嗎?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.