Jay*_*ker 9 java memory text garbage-collection large-files
我正在尝试使用Java将大型文本语料库读入内存.在某些时候,它撞到墙壁,只是垃圾无休止地收集.我想知道是否有人有经验将Java的GC用于提交大型数据集.
我正在读一个8 GB的英文文本文件,用UTF-8,一行写一行.我希望split()在空格上的每一行,并将结果的String数组存储在一个ArrayList<String[]>进行进一步处理.这是一个展示问题的简化程序:
/** Load whitespace-delimited tokens from stdin into memory. */
public class LoadTokens {
private static final int INITIAL_SENTENCES = 66000000;
public static void main(String[] args) throws IOException {
List<String[]> sentences = new ArrayList<String[]>(INITIAL_SENTENCES);
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
long numTokens = 0;
String line;
while ((line = stdin.readLine()) != null) {
String[] sentence = line.split("\\s+");
if (sentence.length > 0) {
sentences.add(sentence);
numTokens += sentence.length;
}
}
System.out.println("Read " + sentences.size() + " sentences, " + numTokens + " tokens.");
}
}
Run Code Online (Sandbox Code Playgroud)
看起来很干脆,对吧?你会注意到我甚至预先调整了我的尺寸ArrayList; 我有少于6600万句话和13亿令牌.现在,如果你掏出你的Java对象大小参考和你的铅笔,你会发现应该需要:
String[]引用@ 8字节ea = 0.5 GBString[]对象@ 32字节ea = 2 GBchar[]对象@ 32字节ea = 2 GBString引用@ 8字节ea = 10 GBStrings @ 44字节ea = 53 GBchars @ 2字节ea = 15 GB83 GB.(您会注意到我确实需要使用64位对象大小,因为压缩OOP无法帮助我获得> 32 GB的堆.)我们很幸运拥有一台带有128 GB RAM的RedHat 6机器,所以我开火了从我的Java SE 1.6.0_29套件中获取我的Java HotSpot(TM)64位服务器VM(构建20.4-b02,混合模式),pv giant-file.txt | java -Xmx96G -Xms96G LoadTokens只是为了安全起见,并在我观看时重新启动top.
在输入不到一半的地方,大约50-60 GB RSS,并行垃圾收集器启动高达1300%的CPU(16个proc盒)并且读取进度停止.然后它会增加几GB,然后进度停止更长时间.它填满了96 GB,尚未完成.我已经放了一个半小时,而且只需要90%的系统时间来烧GC.这看起来很极端.
为了确保我没有疯狂,我掀起了相当的Python(所有两行;)并在大约12分钟和70 GB RSS中完成.
那么:我做的事情是愚蠢的吗?(除了通常效率低下的方式存储,我无法真正帮助 - 即使我的数据结构很胖,只要它们适合,Java就不应该只是窒息.)是否有神奇的GC建议真的很大堆?我确实试过-XX:+UseParNewGC,看起来更糟糕.