Java 메모리 누출을 찾는 방법


142

Java에서 메모리 누수를 어떻게 찾습니까 (예 : JHat 사용)? JHat에서 힙 덤프를로드하여 기본 모양을 보았습니다. 그러나 루트 참조 ( ref ) 또는 호출 된 것을 찾을 수 있어야하는 방법을 이해하지 못합니다 . 기본적으로, 수백 메가 바이트의 해시 테이블 항목 ([java.util.HashMap $ Entry 또는 이와 유사한 것)이 있다고 말할 수 있지만,지도는 어디에서나 사용됩니다 ... 큰지도를 검색 할 방법이 있습니까? 또는 대형 객체 트리의 일반적인 뿌리를 찾을 수 있습니까?

[편집] 좋아, 나는 지금까지 답변을 읽었지만 나는 싼 놈이라고 말하자 (JProfiler를 지불하는 것보다 JHat를 사용하는 방법을 배우는 데 더 관심이 있음을 의미 함). 또한 JHat은 JDK의 일부이므로 항상 사용 가능합니다. 물론 JHat을 사용하는 방법은 없지만 무차별 적 인 방법은 없지만 그럴 수는 없습니다.

또한 실제로 모든 맵 크기의 로깅을 추가 하고 누출을 알 수있을 정도로 오랫동안 실행할 수 있다고 생각하지 않습니다 .


이것은 JProfiler의 또 다른 "투표"입니다. 힙 분석에는 꽤 잘 작동하고 적절한 UI가 있으며 꽤 잘 작동합니다. McKenzieG1이 말했듯이 500 달러는 이러한 누수의 원인을 찾는 데 드는 시간보다 저렴합니다. 도구 가격이 나쁘면 나쁘지 않습니다.
joev September

오라클은 여기에 관련 페이지가 있습니다 : docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/…
Laurel

답변:


126

Java에서 메모리 누수를 찾기 위해 다음 접근법을 사용합니다. 나는 jProfiler를 큰 성공으로 사용했지만, 그래프 기능을 갖춘 모든 특수 도구 (diff는 그래픽 형식으로 분석하기가 더 쉽다)는 생각합니다.

  1. 모든 초기화가 완료되고 응용 프로그램이 유휴 상태 인 경우 응용 프로그램을 시작하고 "안정된"상태가 될 때까지 기다립니다.
  2. 캐시, DB 관련 초기화가 발생할 수 있도록 메모리 누수가 발생하는 것으로 의심되는 작업을 여러 번 실행하십시오.
  3. GC를 실행하고 메모리 스냅 샷을 만듭니다.
  4. 작업을 다시 실행하십시오. 작업의 복잡성과 처리되는 데이터 크기에 따라 작업을 여러 번 실행해야 할 수도 있습니다.
  5. GC를 실행하고 메모리 스냅 샷을 만듭니다.
  6. 2 개의 스냅 샷에 대해 diff를 실행하고 분석하십시오.

기본적으로 분석은 객체 유형에 의한 가장 큰 양의 차이에서 시작하여 추가 객체가 메모리에 달라 붙는 원인을 찾아야합니다.

여러 스레드에서 요청을 처리하는 웹 응용 프로그램의 경우 분석이 더 복잡해 지지만 일반적인 접근 방식은 여전히 ​​적용됩니다.

나는 응용 프로그램의 메모리 풋 프린트를 줄이기 위해 특별히 많은 프로젝트를 수행 했으며이 응용 프로그램 특정 조정 및 트릭 으로이 일반적인 접근 방식은 항상 잘 작동했습니다.


7
대부분의 Java 프로파일 러는 버튼 클릭으로 GC를 호출 할 수있는 옵션을 제공합니다. 또는 코드의 적절한 위치에서 System.gc ()를 호출 할 수 있습니다.
Dima Malenko

3
System.gc ()를 호출하더라도 JVM은 호출을 무시하도록 선택할 수 있습니다. AFAIK JVM 특정입니다. 답변 +1
Aniket Thakur

4
"메모리 스냅 샷"이란 정확히 무엇입니까? 내 코드가 실행중인 각 객체 유형의 수를 알려주는 것이 있습니까?
16:38에

2
"객체 유형별로 가장 큰 양의 차이에서 시작"에서 "추가 개체가 메모리에 걸리는 원인을 찾는 방법"으로 어떻게 이동합니까? int [], Object [], String 등과 같은 매우 일반적인 것을 봅니다. 어디에서 왔는지 어떻게 알 수 있습니까?
Vituel

48

질문자, 클릭에 응답하는 데 5 분이 걸리지 않는 도구를 사용하면 잠재적 인 메모리 누수를 훨씬 쉽게 찾을 수 있습니다.

사람들이 여러 도구를 제안하고 있기 때문에 (JDK 및 JProbe 시험에서 얻은 후에 시각적 wm 만 시도했습니다) Eclipse 플랫폼, 메모리 분석기 (때로는 SAP 메모리라고도 함)에 빌드 된 무료 / 오픈 소스 도구를 제안해야하지만 분석기)를 http://www.eclipse.org/mat/에서 이용할 수 있습니다 .

이 도구의 가장 멋진 점은 처음 열 때 힙 덤프를 인덱싱하여 각 객체에 대해 5 분을 기다리지 않고 보유 힙과 같은 데이터를 표시 할 수 있다는 것입니다 (거의 모든 작업은 내가 시도한 다른 도구보다 훨씬 빠릅니다) .

덤프를 열면 첫 번째 화면에 가장 큰 객체가있는 원형 차트가 표시되고 (계산 된 힙 계산) 하나는 안락을 위해 큰 객체로 빠르게 이동할 수 있습니다. 그것은 또한 내가 생각할 수있는 누출 가능성이 의심되는 찾기를 가지고 있지만 탐색이 충분했기 때문에 실제로 들어 가지 않았습니다.


1
주목할만한 점은 분명히 Java 5 이상에서는 HeapDumpOnCtrlBreakVM 매개 변수 를 사용할 수 없다는 것 입니다. 내가 찾은 해결책 (지금까지도 여전히 찾고 있음)은 JMap을 사용하여 .hprof파일 을 덤프 한 다음 Eclipse에 넣고 MAT를 사용하여 검사합니다.
Ben

1
힙 덤프 확보와 관련하여 대부분의 프로파일 러 (JVisualVM 포함)에는 힙과 스레드를 모두 파일로 덤프하는 옵션이 있습니다.
bbaja42

13

도구는 큰 도움이됩니다.

그러나 도구를 사용할 수없는 경우가 있습니다. 힙 덤프가 너무 커서 도구와 충돌하고 일부 프로덕션 환경에서 셸 액세스 권한 만있는 컴퓨터의 문제를 해결하려고합니다.

이 경우 hprof 덤프 파일을 처리하는 방법을 아는 것이 도움이됩니다.

BEGIN SITES를 찾으십시오. 이것은 어떤 객체가 가장 많은 메모리를 사용하고 있는지 보여줍니다. 그러나 객체는 유형별로만 묶이지 않습니다. 각 항목에는 "추적"ID도 포함됩니다. 그런 다음 해당 "TRACE nnnn"을 검색하여 객체가 할당 된 스택의 최상위 몇 프레임을 볼 수 있습니다. 종종 객체가 할당 된 위치를 확인하면 버그를 발견하고 완료됩니다. 또한 -Xrunhprof 옵션을 사용하여 스택에 기록되는 프레임 수를 제어 할 수 있습니다.

할당 사이트를 확인한 후 잘못된 것이 없으면 예상하지 못한 참조 체인을 찾기 위해 해당 라이브 오브젝트 중 일부에서 루트 오브젝트로 역방향 체인을 시작해야합니다. 이것은 도구가 실제로 도움이되는 곳이지만 손으로도 같은 일을 할 수 있습니다 (그렙으로). 루트 객체는 하나도 없습니다 (즉, 가비지 수집 대상이 아닌 객체). 스레드, 클래스 및 스택 프레임은 루트 객체의 역할을하며 이들이 참조하는 것은 수집 할 수 없습니다.

체인을 수행하려면 HEAP DUMP 섹션에서 불량 추적 ID가있는 항목을 찾으십시오. OBJ 또는 ARR 항목으로 이동하여 16 진수로 고유 한 오브젝트 ID를 표시합니다. 해당 ID의 모든 항목을 검색하여 객체에 대한 강력한 참조를 가진 사람을 찾으십시오. 누출이있는 곳을 알아낼 때까지 분기 된 각 경로를 뒤로 따르십시오. 도구가 왜 그렇게 편리한 지 알아?

정적 멤버는 메모리 누수에 대한 반복적 인 위반자입니다. 실제로 도구가 없어도 정적 Map 멤버의 코드를 살펴 보는 데 몇 분이 소요됩니다. 지도가 커질 수 있습니까? 항목을 정리 한 것이 있습니까?


내가 체크 - 마지막 "힙 덤프는 도구를 충돌이 너무 크다" jhatMAT분명히 메모리에 전체 힙 덤프를로드하려고, 그래서 일반적으로 충돌이 OutOfMemoryError가장 힙 분석을 필요로하는 응용 프로그램에서, 즉 대형 덤프 (에! ). NetBeans Profiler는 인덱싱 참조에 다른 알고리즘을 사용하는 것으로 보이며 , 큰 덤프에서는 느려질 수 있지만 최소한 도구에서 무한 메모리를 소비하지 않으며 충돌합니다.
Jesse Glick

10

대부분의 경우 엔터프라이즈 응용 프로그램에서 제공된 Java 힙은 최대 12-16GB의 이상적인 크기보다 큽니다. NetBeans 프로파일 러가 이러한 큰 Java 앱에서 직접 작동하는 것이 어렵다는 것을 알았습니다.

그러나 보통 이것은 필요하지 않습니다. jdk와 함께 제공되는 jmap 유틸리티를 사용하여 "실시간"힙 덤프를 수행 할 수 있습니다. 즉, jmap은 GC를 실행 한 후 힙을 덤프합니다. 응용 프로그램에서 일부 작업을 수행하고 작업이 완료 될 때까지 기다린 다음 다른 "라이브"힙 덤프를 가져 오십시오. Eclipse MAT와 같은 도구를 사용하여 힙 덤프를로드하고, 히스토그램을 정렬하고, 증가한 오브젝트 또는 가장 높은 오브젝트를 확인하십시오. 이것이 실마리를 제공합니다.

su  proceeuser
/bin/jmap -dump:live,format=b,file=/tmp/2930javaheap.hrpof 2930(pid of process)

이 방법에는 한 가지 문제 만 있습니다. 라이브 옵션을 사용하더라도 대용량 힙 덤프는 개발 랩으로 전송하기에는 너무 크며 메모리 / RAM이 충분한 머신이 필요할 수 있습니다.

그것이 클래스 히스토그램이 그려지는 곳입니다. jmap 도구를 사용하여 라이브 클래스 히스토그램을 덤프 할 수 있습니다. 이 클래스는 메모리 사용에 대한 클래스 히스토그램 만 제공합니다. 기본적으로 참조를 연결하는 정보는 없습니다. 예를 들어 char 배열을 맨 위에 놓을 수 있습니다. 그리고 아래 어딘가에 String 클래스가 있습니다. 연결을 직접 그려야합니다.

jdk/jdk1.6.0_38/bin/jmap -histo:live 60030 > /tmp/60030istolive1330.txt

두 개의 힙 덤프를 사용하는 대신 위에서 설명한 것처럼 두 개의 클래스 히스토그램을 사용하십시오. 그런 다음 클래스 히스토그램을 비교하고 증가하는 클래스를 확인하십시오. Java 클래스를 애플리케이션 클래스와 연관시킬 수 있는지 확인하십시오. 이것은 꽤 좋은 힌트를 줄 것입니다. 다음은 두 개의 jmap 히스토그램 덤프를 비교하는 데 도움이되는 pythons 스크립트입니다. histogramparser.py

마지막으로 JConolse 및 VisualVm과 같은 도구는 시간이 지남에 따라 메모리 증가를 확인하고 메모리 누수가 있는지 확인하는 데 필수적입니다. 마지막으로 문제는 메모리 누수가 아닌 메모리 사용량 일 수 있습니다.이를 위해 GC 로깅을 사용하도록 설정하십시오. jstat와 같은 jdk 도구를 사용하여 GC 동작을 실시간으로 볼 수 있습니다.

jstat -gccause pid <optional time interval>

-jhat, jmap, Full GC, Humongous 할당, G1GC에 대한 Google의 다른 참조


1
자세한 내용은 여기에 블로그 게시물을 추가 -alexpunnen.blogspot.in/2015/06/…
Alex Punnen 2016 년

5

JProbe, YourKit, AD4J 또는 JRockit Mission Control과 같이 누출을 찾는 데 도움이되는 도구가 있습니다. 마지막은 내가 개인적으로 가장 잘 알고있는 것입니다. 좋은 도구를 사용하면 누수를 쉽게 식별 할 수있는 수준과 누수 대상이 할당 된 위치까지 드릴 다운해야합니다.

HashTables, Hashmaps 또는 이와 유사한 기능을 사용하면 Java에서 메모리를 전혀 누수 할 수있는 몇 가지 방법 중 하나입니다. 손으로 누출을 찾아야한다면 나는 정기적으로 내 HashMaps의 크기를 인쇄하고 거기에서 항목을 추가하고 삭제하는 것을 잊어 버렸습니다.


4

글쎄, 맵을 수정할 때 맵 크기의 로깅을 추가 한 다음, 맵이 적당한 크기 이상으로 커지는 로그를 검색하는 최첨단 솔루션이 항상 있습니다.


1

NetBeans에는 내장 프로파일 러가 있습니다.


0

할당을 추적하는 메모리 프로파일 러를 사용해야합니다. JProfiler를 살펴보십시오. "힙 워커"기능은 훌륭하며 모든 주요 Java IDE와 통합되어 있습니다. 무료는 아니지만 비싸지 않습니다 (단일 라이센스의 경우 499 달러)-덜 복잡한 도구로 누수를 찾기 위해 500 달러의 시간을 꽤 빨리 고생시킬 것입니다.


0

가비지 수집기를 여러 번 호출 한 후 메모리 사용 크기를 측정하여 확인할 수 있습니다.

Runtime runtime = Runtime.getRuntime();

while(true) {
    ...
    if(System.currentTimeMillis() % 4000 == 0){
        System.gc();
        float usage = (float) (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024;
        System.out.println("Used memory: " + usage + "Mb");
    }

}

출력 수가 같으면 응용 프로그램에 메모리 누수가 없지만 메모리 사용 횟수 (수 증가)가 다른 경우 프로젝트에 메모리 누수가있는 것입니다. 예를 들면 다음과 같습니다.

Used memory: 14.603279Mb
Used memory: 14.737213Mb
Used memory: 14.772224Mb
Used memory: 14.802681Mb
Used memory: 14.840599Mb
Used memory: 14.900841Mb
Used memory: 14.942261Mb
Used memory: 14.976143Mb

참고 때로는 스트림 소켓과 같은 일부 작업에 의해 릴리스 메모리에 약간의 시간이 소요됩니다. 첫 번째 출력으로 판단해서는 안되며 특정 시간에 테스트해야합니다.


0

JProfiler로 메모리 누수를 찾는 방법에 대한 이 화면을 확인하십시오 . @Dima Malenko Answer의 시각적 설명입니다.

참고 : JProfiler는 프리웨어가 아니지만 시험판 버전은 현재 상황을 처리 할 수 ​​있습니다.


0

우리 대부분은 이미 코드를 작성하기 위해 Eclipse를 사용하고 있으므로 Eclipse에서 MAT (Memory Analyzer Tool)를 사용하지 않는 이유는 무엇입니까? 잘 작동합니다.

이클립스 MAT는 분석 도구를 제공하는 이클립스 IDE를위한 플러그인의 집합입니다 heap dumps자바 응용 프로그램 및 식별하는 memory problems응용 프로그램에서.

이를 통해 개발자는 다음 기능으로 메모리 누수를 찾을 수 있습니다.

  1. 메모리 스냅 샷 획득 (힙 덤프)
  2. 히스토그램
  3. 유지 된 힙
  4. 지배자 트리
  5. GC 루트로의 경로 탐색
  6. 조사관
  7. 공통 메모리 안티 패턴
  8. 객체 쿼리 언어

여기에 이미지 설명을 입력하십시오

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.