【问题标题】:External Sort GC Overhead外部排序 GC 开销
【发布时间】:2017-08-14 18:53:34
【问题描述】:

我正在编写一个外部排序来对磁盘上的一个大 2 gig 文件进行排序

我首先将文件拆分为适合内存的块,并对每个块进行单独排序,然后将它们重写回磁盘。但是,在此过程中,我在函数 geModel 的 String.Split 方法中遇到 GC 内存开销异常。下面是我的代码。

private static List<Model> getModel(String file, long lineCount, final long readSize) {
    List<Model> modelList = new ArrayList<Model>();
    long read = 0L;
    try (BufferedReader br = new BufferedReader(new FileReader(file))) {
        //Skip lineCount lines;
        for (long i = 0; i < lineCount; i++)
            br.readLine();
        String line = "";
        while ((line = br.readLine()) != null) {
            read += line.length();
            if (read > readSize)
                break;
            String[] split = line.split("\t");
            String curvature = (split.length >= 7) ? split[6] : "";
            String heading = (split.length >= 8) ? split[7] : "";
            String slope = (split.length == 9) ? split[8] : "";

            modelList.add(new Model(split[0], split[1], split[2], split[3], split[4], split[5], curvature, heading, slope));
        }   
        br.close();
        return modelList;
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return null;
}

private static void split(String inputDir, String inputFile, String outputDir, final long readSize) throws IOException {
    long lineCount = 0L;
    int count = 0;
    int writeSize = 100000;
    System.out.println("Reading...");
    List<Model> curModel = getModel(inputDir + inputFile, lineCount, readSize);
    System.out.println("Reading Complete");
    while (curModel.size() > 0) {
        lineCount += curModel.size();
        System.out.println("Sorting...");
        curModel.sort(new Comparator<Model>() {
            @Override
            public int compare(Model arg0, Model arg1) {
                return arg0.compareTo(arg1);
            }
        });
        System.out.println("Sorting Complete");
        System.out.println("Writing...");
        writeFile(curModel, outputDir + inputFile + count, writeSize);
        System.out.println("Writing Complete");
        count++;
        System.out.println("Reading...");
        curModel = getModel(inputDir + inputFile, lineCount, readSize);
        System.out.println("Reading Complete");
    }
}

它通过一次并从文件中排序约 250 MB 的数据。但是,在第二遍时,它会在 String.split 函数上引发 GC 内存开销异常。我不想使用外部库,我想自己学习。排序和拆分工作,但我不明白为什么 GC 在 string.split 函数上抛出内存开销异常。

【问题讨论】:

  • 你能发布每行有什么样的数据吗?正如比尔在他的回答中提到的那样,您有多种方式来调试或分析它。我不明白的一件事是模型对象/类在做什么。根据我的理解。您的文件似乎是由制表符分隔的单词,而且您很可能每行只有 8 个单词。
  • 您需要查找如何正确进行外部排序。这不是。您需要通过替换选择来分配初始运行,然后进行多相或平衡合并。
  • @EJP 我知道还有其他方法可以优化这种排序。但是,这种排序确实给了我排序的数据。输出很好,但可能不如具有多个合并阶段的实际外部排序最佳。这个只有一个合并阶段,它的 IO 操作比优化的合并排序要多得多。你同意我写的吗?
  • 我想这很难说,因为我没有在问题中发布合并部分。基本上我会抓取一些适合内存的数据,对其进行排序,将其写入新文件,对整个文件重复此过程。拆分完成后,我运行我的合并部分,然后对我的所有拆分文件进行 k-way-merge 排序,就像合并排序中的合并一样,直到我们有一个大的排序文件

标签: java sorting external-sorting


【解决方案1】:

我不确定是什么导致了异常——操作大字符串,特别是剪切和拼接它们,是一个巨大的内存/gc 问题。 StringBuilder 可以提供帮助,但通常您可能需要更直接地控制该过程。

要了解更多信息,您可能希望在您的应用中运行分析器。 JDK (VisualVM) 中内置了一个功能。它将向您展示 Java 持有哪些对象...由于字符串的性质,您可能持有大量冗余字符数组数据。

我个人会尝试一种完全不同的方法,例如,如果您通过将每行的前 10(?)个可排序字符以及读取它们的文件位置加载到一个数组中来对内存中的整个文件进行排序怎么办,对数组进行排序并通过加载更多(其余的?)相同的行来解决任何关系。

如果您做了类似的事情,那么您应该能够查找每一行并将其复制到目标文件,而无需在内存中缓存超过一行,并且只读取源文件两次。

我想你可以制造一个文件,如果所有字符串在最后几个字符之前都是相同的,那么这个文件会失败,所以如果这成为一个问题,你可能必须能够刷新你缓存的完整字符串(有一个java内存引用对象自动为你做这件事,这不是特别难)

【讨论】:

    【解决方案2】:

    根据我如何阅读您的实现 readSize 仅确保您获得第一个块 X 大小。您没有阅读第二块或第三块。因此它实际上并不是完整的外部排序。

     read += line.length();
            if (read > readSize)
                break;
    String[] split = line.split("\t");
    

    即使您拆分每一行,您似乎只使用前 9 个字符。然后检查每行中没有单词。这意味着您的数据不统一。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-21
      • 2017-12-27
      • 2013-07-13
      • 2018-03-29
      • 2012-04-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多