2016-07-27 3 views
1

를 사용하여 파일의 "마지막 행"나는 다음과 같은 데이터 구조의 두 가지 간단한 샘플 파일이 읽기 :
person.csv자바 IO : 메모리/성능 문제의 BufferedReader

0|John 
1|Maria 
2|Anne 

항목을 .CSV

0|car|blue 
0|bycicle|red 
1|phone|gold 
2|purse|black 
2|book|black 

I 모든 관련 라인 수집 할 필요가 (본 예에서는 동일한 ID를 가진 라인을, 정수 0, 1 또는 2) 모든 파일 및 그들을 모으고 나서 (그들과 무관 한) 무언가를하십시오. 관련된 라인 (문자열리스트)의 첫 번째 그룹은 다음과 같이 표시한다 :

0|John 
0|car|blue 
0|bycicle|red 

관련 행의 두 번째 그룹 :

1|Maria 
1|phone|gold 

실제 파일 5됩니다 각 파일 당 10GB. 파일은 첫 번째 열에서 정렬되고 가장 작은 ID를 가진 파일이 먼저 열리도록 열립니다. 메모리는 제한 요소입니다 (메모리의 전체 파일을 읽을 수는 없습니다). 그 점을 염두에두고 다음 코드를 작성하여 대부분의 행을 읽은 다음 원하는대로 그룹화하는 것으로 보입니다. 그러나 마지막 부분 (250.000 그룹의 로깅 수를 설정하는 코드)은 훨씬 오래 걸리고 메모리 사용량이 급증합니다.

홈페이지

public class Main { 

    private static int groupCount = 0; 
    private static int totalGroupCount = 0; 
    private static long start = 0; 
    private static int lineCount; 

    public static void main(String[] args) { 
     GroupedReader groupedReader = new GroupedReader(); 
     groupedReader.orderReadersOnSmallestId(); 
     long fullStart = System.currentTimeMillis(); 
     start = System.currentTimeMillis(); 
     lineCount = 0; 
     while (groupedReader.hasNext()) { 
      groupCount++; 
      List<String> relatedLines = groupedReader.readNextGroup(); 
      for (String line : relatedLines) { 
       lineCount++; 
      } 
      totalGroupCount++; 
      if (groupCount == 250_000) { 
       System.out.println("Building " + NumberFormat.getNumberInstance(Locale.US).format(groupCount) + " groups took " + (System.currentTimeMillis() - start)/1e3 + " sec"); 
       groupCount = 0; 
       start = System.currentTimeMillis(); 
      } 
     } 
     System.out.println("Building " + NumberFormat.getNumberInstance(Locale.US).format(groupCount) + " groups took " + (System.currentTimeMillis() - start)/1e3 + " sec"); 
     System.out.println(String.format("Building [ %s ] groups from [ %s ] lines took %s seconds", NumberFormat.getNumberInstance(Locale.US).format(totalGroupCount), NumberFormat.getNumberInstance(Locale.US).format(lineCount), (System.currentTimeMillis() - fullStart)/1e3)); 
     System.out.println("all done!"); 
    } 
} 

GroupedReader이 ... 몇 가지 방법

public class GroupedReader { 

    private static final String DELIMITER = "|"; 
    private static final String INPUT_DIR = "src/main/resources/"; 

    private boolean EndOfFile = true; 
    private List<BufferedReader> sortedReaders; 
    private TreeMap<Integer, List<String>> cachedLines; 
    private List<String> relatedLines; 
    private int previousIdentifier; 

    public boolean hasNext() { 
     return (sortedReaders.isEmpty()) ? false : true; 
    } 

    public List<String> readNextGroup() { 
     updateCache(); 
     EndOfFile = true; 
     for (int i = 0; i < sortedReaders.size(); i++) { 
      List<String> currentLines = new ArrayList<>(); 
      try { 
       BufferedReader br = sortedReaders.get(i); 
       for (String line; (line = br.readLine()) != null;) { 
        int firstDelimiterIndex = StringUtils.ordinalIndexOf(line, DELIMITER, 1); 
        int currentIdentifier = Integer.parseInt(line.substring(0, firstDelimiterIndex)); 
        if (previousIdentifier == -1) { 
         // first iteration 
         previousIdentifier = currentIdentifier; 
         relatedLines.add(i + DELIMITER + line); 
         continue; 
        } else if (currentIdentifier > previousIdentifier) { 
         // next identifier, so put the lines in the cache 
         currentLines.add(i + DELIMITER + line); 
         if (cachedLines.get(currentIdentifier) != null) { 
          List<String> local = cachedLines.get(currentIdentifier); 
          local.add(i + DELIMITER + line); 
         } else { 
          cachedLines.put(currentIdentifier, currentLines); 
         } 
         EndOfFile = false; 
         break; 
        } else { 
         // same identifier 
         relatedLines.add(i + DELIMITER + line); 
        } 
       } 
       if (EndOfFile) { 
        // is this close needed? 
        br.close(); 
        sortedReaders.remove(br); 
       } 
      } catch (NumberFormatException | IOException e) { 
       e.printStackTrace(); 
      } 
     } 
     if (cachedLines.isEmpty()) cachedLines = null; 
     return relatedLines; 
    } 

    private void updateCache() { 
     if (cachedLines != null) { 
      previousIdentifier = cachedLines.firstKey(); 
      relatedLines = cachedLines.get(cachedLines.firstKey()); 
      cachedLines.remove(cachedLines.firstKey()); 
     } else { 
      previousIdentifier = -1; 
      relatedLines = new ArrayList<>(); 
      cachedLines = new TreeMap<>(); 
      // root of all evil...? 
      System.gc(); 
     } 
    } 
} 

내가 시도

을 ommited '명시 적으로 폐쇄 독자들과 주위를 "재생"및 가비지 콜렉터를 호출,하지만 난 할 수 내가 작성한 코드의 실제 결함을 발견하지 못한다.

질문 :
파일 끝 부분의 읽기 속도가 느려지는 원인은 무엇입니까?

간단한 syso 로그 :

Building 250,000 groups took 0.394 sec 
Building 250,000 groups took 0.261 sec 
Building 250,000 groups took 0.289 sec 
... 
Building 250,000 groups took 0.281 sec 
Building 250,000 groups took 0.314 sec 
Building 211,661 groups took 10.829 sec 
Building [ 9,961,661 ] groups from [ 31,991,125 ] lines took 21.016 seconds 
all done! 

답변

0

System.gc()는 요청입니다하지만 GC가 일어날 것이다 보장하지.

시간을 빨리 볼 수있게하려면 코드의 더 많은 지점에서 더 많은 로깅을 추가하고 groupCount를 줄이면 더 나은 시간 (10000?)을 볼 수 있습니다.

프로필을 제대로 작성하고 더 잘 이해하려면 JDK와 함께 제공되는 도구 (예 : visualvm 또는 mission control)를 사용하십시오.

둘 다 JDK 설치의 bin 폴더에서 찾을 수 있습니다.

+0

'System.gc()'는 실험적 코드 였지만 사용하지 않으려 고합니다.프로파일 링 옵션을 시험해 보겠습니다. 그러나 제 질문에 대한 직접적인 대답이 아니므로 대답을 수락 할 수 없습니다. –

+0

구체적인 대답을 원할 경우 전체 Java 클래스와 데이터 파일의 전체 덤프를 게시해야합니다. 이것이 진정한 특질 일 경우, 제공된 것과 함께 발견되지 않을 수도 있습니다. 공구와 함께 행운을 빈다. – UserF40