我正在尝试使用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.");
}
}
看起来很干,对吗?您会注意到我什至对我的东西进行了预先调整ArrayList
;我的句子少于6600万个,令牌少于13亿个。现在,如果您不妨参考一下Java对象大小参考和铅笔,那么您将发现,这大约需要:
String[]
引用@ 8个字节ea = 0.5 GBString[]
对象@ 32个字节ea = 2 GBchar[]
对象@ 32个字节ea = 2 GBString
引用@ 8个字节ea = 10 GBString
s @ 44个字节ea = 53 GBchar
s @ 2个字节ea = 15 GB83 GB。(您会注意到我确实确实需要使用64位对象大小,因为压缩的OOP无法帮助我处理> 32 GB的堆。)我们很幸运地拥有一台具有128 GB RAM的RedHat 6机器,所以我开了pv giant-file.txt | java -Xmx96G -Xms96G LoadTokens
为了安全起见,从Java SE 1.6.0_29套件中安装了Java HotSpot(TM)64位服务器VM(内部版本20.4-b02,混合模式),然后在观看时反冲top
。
在不到输入一半的地方,大约有50-60 GB RSS,并行垃圾收集器启动了1300%的CPU(16个proc盒),并且读取进度停止。然后它又增加了几GB,然后进度停止了更长的时间。它已填满96 GB,但尚未完成。我已经放了一个半小时了,做GC只消耗了大约90%的系统时间。那似乎是极端的。
为了确保我没有发疯,我整理了等效的Python(共两行;),它在大约12分钟和70 GB的RSS中运行完毕。
所以:我在做些蠢事吗?(除了通常低效的存储方式之外,我什至无济于事-即使我的数据结构很胖,只要它们适合,Java也不会令人窒息。)是否有神奇的GC建议?大堆?我确实尝试过-XX:+UseParNewGC
,而且看起来甚至更糟。
-XX:+UseConcMarkSweepGC
:在78 GB和12分钟内完成。(几乎与Python一样好!)谢谢大家的帮助。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句