2017-08-14 55 views
0

我写的外部排序整理磁盘外部排序GC开销

大2演出文件我首先拆分文件到装入内存,并单独排序每一个数据块,并重写他们回磁盘。但是,在这个过程中,我在函数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 Memory Overhead异常。我不想使用外部库,我想自己学习。排序和分裂工程,但我不明白为什么GC在string.split函数抛出内存开销异常。

+0

你可以发布每行有什么样的数据。正如Bill在他的回答中提到的,您可以通过多种方式进行调试或分析。有一件事我不明白的是Model对象/类正在做什么。基于我的理解。您的文件似乎是由制表符分隔的单词,并且您很可能每行只有8个单词。 – Acewin

+1

您需要查看如何正确进行外部排序。这不是。你需要做的替代选择初始运行,随后进行多相或合并均衡分布。 – EJP

+0

@EJP我知道还有更多的方法来优化这种排序。但是,这种确实给了我排序的数据。输出很好,但可能不如具有多个合并阶段的实际外部排序那样优化。这一个只有一个合并阶段,它有更多的IO操作,而不是优化的合并排序。你会同意我写的吗? – jLangley011

回答

0

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

要想知道更多,您可能需要使用您的应用运行分析器。有一个内置于JDK(VisualVM)的功能。它将向您展示Java持有的对象......因为字符串的性质,您可能会持有大量冗余字符数组数据。

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

如果你没有类似的东西,那么你应该能够寻求到的每一行,并将其复制到目标文件而不用缓存在内存中多行只有通过源文件中读取两次。

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

0

根据我如何阅读你的实现readSize只确保你得到第一个块X的大小。你不读第二块或第三块。因此它并不是完全的外部排序。

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

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