【问题标题】:Java - OutofMemoryError while reading a huge csv fileJava - 读取巨大的 csv 文件时出现 OutofMemoryError
【发布时间】:2014-09-30 20:04:14
【问题描述】:

我想通过 Java 读取一个巨大的 csv 文件。它包括 75,000,000 行。问题是,即使我使用最大 xmsxmx 限制,我得到:`java.lang.OutOfMemoryError(GC 开销限制超出),它显示此行导致错误:

String[][] matrix = new String[counterRow][counterCol];

我做了一些测试,发现我可以很好地阅读 15,000,000 行。因此我开始使用这种代码:

String csvFile = "myfile.csv";
List<String[]> rowList = new ArrayList();
String line = "";
String cvsSplitBy = ",";
BufferedReader br = null;
try {
    int counterRow = 0, counterCol = 12, id = 0;
    br = new BufferedReader(new FileReader(csvFile));
    while ((line = br.readLine()) != null) { 
        String[] object = line.split(cvsSplitBy);
        rowList.add(object); 
        counterRow++;
        if (counterRow % 15000000 ==0) {
            String[][] matrix = new String[counterRow][counterCol];
            .. do processes ..
            SaveAsCSV(matrix,id);
            counterRow=0; id++; rowList.clear();
        }
    }
}
...

在这里,前 15.000.000 行写得很好,但在第二次试验中,这再次给出了同样的错误,尽管 counterRow 是 15,000,000。

总之,我需要在 Java 中读取一个包含 75,000,000 行(约 5 GB)的csv 文件,并在对其记录进行一些处理后保存一个新的csv 文件或多个文件。

我该如何解决这个问题?

谢谢

编辑:我也在使用 rowList.clear() 家伙,忘了在这里指定。对不起。

编辑 2:我的朋友们,我不需要将所有文件都放在内存中。我怎样才能部分地阅读它。实际上,这就是我尝试使用 if(counterRow%15000000==0) 来做的事情。它的正确方法是什么?

【问题讨论】:

  • 内存中的数据量非常大 - 为什么不尝试写入数据库,然后查询呢?
  • 你绝对不能把整个该死的文件都带入内存。可以分批/分批处理文件吗?
  • 如果你的文件是 5GB 并且你想把它保存在内存中,我认为你需要 5 GB 的 RA%,巨大的 ^^
  • streaming 是你这里最好的朋友

标签: java csv memory


【解决方案1】:

您可以单独阅读这些行,然后进行处理,直到您阅读了整个文件

String encoding = "UTF-8";
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("/bigfile.txt"), encoding));
String line;
while ((line = br.readLine()) != null) {
   // process the line.
}
br.close();

这不应该只是确保您立即处理它并且不要将其存储在循环之外的变量中

【讨论】:

  • 我很快就会试试这个,让你知道结果我的朋友。谢谢
  • 效果很好,我的朋友!非常感谢!
【解决方案2】:

问题不在于您没有足够的内存,“超出 GC 开销限制”的问题意味着垃圾收集花费的时间太长。您无法通过分配更多内存来解决此问题,只能使用-XX:-UseGCOverheadLimit。也就是说,如果你真的想要内存中有那么多数据。

参见例如How to solve "GC overhead limit exceeded" using maven jvmArg?

或者使用 peter lawrey 的内存映射 HugeCollections:http://vanillajava.blogspot.be/2011/08/added-memory-mapped-support-to.html?q=huge+collections:如果内存已满,它会写入磁盘。

【讨论】:

  • 啊,好点。我也在使用 rowList.clear(),忘了在这里复制/粘贴!
【解决方案3】:

也许你忘了打电话

rowList.clear();

之后

counterRow=0; id++;

【讨论】:

  • 啊,好点。我也在使用 rowList.clear(),忘记在这里复制/粘贴!
【解决方案4】:

当您的应用程序几乎耗尽所有可用内存并且 GC 多次无法清理它时,将显示“java.lang.OutOfMemoryError: GC overhead limit exceeded”错误。

上面推荐的解决方案 - 指定 -XX:-UseGCOverheadLimit 是我强烈建议不要做的事情。你只是推迟了不可避免的问题,而不是解决问题:应用程序内存不足,需要修复。指定这个选项只会用更熟悉的消息“java.lang.OutOfMemoryError: Java heap space”掩盖原来的“java.lang.OutOfMemoryError: GC overhead limit exceeded”错误.

在您的情况下,可能的解决方案几乎可以归结为两种合理的选择 - 增加堆空间(-Xmx 参数)或通过以较小的批次读取文件来减少代码的堆消耗。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-01
    • 2021-05-29
    • 1970-01-01
    • 1970-01-01
    • 2020-08-05
    相关资源
    最近更新 更多