外部排序 GC 开销



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

我首先将文件拆分为适合内存的块,并分别对每个块进行排序,然后将它们重写回磁盘。但是,在此过程中,我在函数 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 函数上抛出内存开销异常。

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

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

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

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

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

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

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

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

相关内容

最新更新