문제

내가 일하는 곳에서는 파일당 백만 행이 넘는 파일이 있었습니다.서버 메모리가 10GB 이상(JVM의 경우 8GB임에도 불구하고 서버가 잠시 중단되어 다른 작업이 중단되는 경우가 있습니다.)

코드를 프로파일링한 결과 파일을 읽는 동안 메모리 사용량이 기가바이트 단위로 자주(1GB~3GB) 증가했다가 갑자기 정상으로 돌아오는 것을 발견했습니다.높은 메모리와 낮은 메모리를 자주 사용하면 서버가 중단되는 것 같습니다.물론 이는 가비지 컬렉션 때문이었습니다.

더 나은 성능을 위해 파일을 읽으려면 어떤 API를 사용해야 합니까?

지금 나는 사용하고 있습니다 BufferedReader(new FileReader(...)) 이 CSV 파일을 읽으려면

프로세스 :파일을 어떻게 읽고 있나요?

  1. 파일을 한 줄씩 읽습니다.
  2. 모든 줄에는 몇 개의 열이 있습니다.유형에 따라 그에 따라 구문 분석합니다(double의 비용 열, int의 방문 열, 문자열의 키워드 열 등).
  3. HashMap에 적합한 콘텐츠(방문 > 0)를 푸시하고 작업이 끝나면 최종적으로 해당 맵을 지웁니다.

업데이트

30~31개의 파일(1개월 데이터)을 읽고 해당 항목을 지도에 저장합니다.나중에 이 맵은 다른 테이블에서 일부 범인을 가져오는 데 사용됩니다.따라서 읽는 것도 필수이고, 해당 데이터를 저장하는 것도 필수입니다.지금은 HashMap 부분을 BerkeleyDB로 전환했지만 파일을 읽을 때의 문제는 동일하거나 더 나쁩니다.

도움이 되었습니까?

해결책

BufferedReader는 이를 위해 사용할 수 있는 두 가지 최고의 API 중 하나입니다.파일을 읽는 데 정말로 문제가 있다면 대안은 다음의 내용을 사용하는 것입니다. 니오 파일을 메모리 매핑한 다음 메모리에서 직접 내용을 읽습니다.

그러나 당신의 문제는 독자에게 있는 것이 아닙니다.문제는 모든 읽기 작업이 여러 개의 새로운 개체를 생성한다는 것입니다. 이는 대부분 읽기 직후에 수행하는 작업에서 발생합니다.

생성하는 개체의 수 및/또는 크기를 줄이거나 더 이상 필요하지 않은 개체를 더 빨리 제거하는 데 중점을 두고 입력 처리를 정리하는 것을 고려해야 합니다.처리를 위해 파일 전체를 메모리에 흡입하는 대신 한 번에 한 줄 또는 한 덩어리씩 파일을 처리하는 것이 가능할까요?

또 다른 가능성은 가비지 수집을 조작하는 것입니다.두 가지 메커니즘이 있습니다.

  • 10초마다 또는 1000개의 입력 라인마다 등 가끔씩 명시적으로 가비지 수집기를 호출합니다.이렇게 하면 GC에서 수행하는 작업량이 늘어나지만 각 GC에 소요되는 시간이 줄어들고 메모리가 많이 늘어나지 않으므로 서버의 나머지 부분에 미치는 영향이 줄어들기를 바랍니다.

  • JVM의 가비지 수집기 옵션을 조정해 보세요.이는 JVM마다 다르지만 java -X 몇 가지 힌트를 주어야합니다.

업데이트: 가장 유망한 접근 방식:

처리를 위해 한 번에 메모리에 전체 데이터 세트가 필요합니까?

다른 팁

코드를 프로파일 링 한 결과 파일 읽기 메모리 사용이 Giga 바이트에서 자주 상승하는 동안 (1GB ~ 3GB) 갑자기 정상으로 돌아옵니다. 이 빈번한 높고 낮은 메모리 사용은 내 서버를 걸어 다니는 것 같습니다. 물론 이것은 쓰레기 수집 때문이었습니다.

사용 BufferedReader(new FileReader(...)) 원인이되지 않습니다.

문제는 라인/행을 배열 또는 목록에 읽고 처리 한 다음 배열/목록을 버리고 있다고 생각합니다. 이렇게하면 메모리 사용이 증가한 다음 다시 감소합니다. 이 경우 읽을 때 각 라인/행을 처리하여 메모리 사용량을 줄일 수 있습니다.

편집하다: 우리는 문제가 메모리의 파일 내용을 나타내는 데 사용되는 공간에 관한 것이라는 데 동의합니다. 엄청난 메모리 해시 테이블의 대안은 컴퓨터 메모리를 KBYTES에서 측정했을 때 사용한 오래된 "정렬 병합"접근법으로 돌아가는 것입니다. (나는 처리가 Keys K와의 조회를 수행하는 단계에 의해 관련 행 R을 얻는 단계에 의해 지배적이라고 가정합니다.)

  1. 필요한 경우 각 입력 파일을 키 K에서 정렬 할 수 있도록 각 입력 파일을 전제로 처리하십시오.

  2. 효율적인 파일 정렬 유틸리티를 사용하여 모든 입력 파일을 K에서 순서대로 정렬하십시오. 클래식 병합 정렬 알고리즘을 사용하는 유틸리티를 사용하려고합니다. 이렇게하면 각 파일을 메모리에서 정렬 할 수있는 작은 청크로 분할하고 청크를 정렬하고 임시 파일로 작성 한 다음 정렬 된 임시 파일을 병합합니다. 유닉스 / 리눅스 sort 유틸리티는 좋은 선택입니다.

  3. 정렬 된 파일을 병렬로 읽고 모든 파일의 각 키 값과 관련된 모든 행을 읽고 처리 한 다음 다음 키 값으로 밟습니다.

사실, 나는 BerkeleyDB를 사용하는 것이 도움이되지 않았다는 것에 약간 놀랐습니다. 그러나 프로파일 링이 DB를 구축하는 데 대부분의 시간이 DB를 구축했다고 말하면 DB를 구축하기 전에 입력 파일 (위와 같이)을 오름차순 키 순서로 정렬하여 속도를 높일 수 있습니다. (큰 파일 기반 색인을 만들 때 항목이 키 순서로 추가되면 더 나은 성능을 얻을 수 있습니다.)

GC를 조정하고 GC 인쇄를 수행하려면 다음 VM 옵션을 사용해보십시오.

-verbose:gc -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top