ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java Heap Dump
    Java/Basics 2020. 6. 15. 22:18


    ■Memory Leak


    프로그램이 OOM(Out Of Memory) 오류로 종료되거나 실행 속도가 저하되는 현상.


    자주 발생하는 OOM 2가지


    1. java.lang.OutOfMemorryError: heap space


    2. java.lang.OutOfMemorryError: GC Overhead limit exceed


    출처에 따르면, 자바 프로세스가 약 98%의 시간을 가비지 컬렉터를 실행하는데 소모하고


    실행 이후에도 2% 이상의 힙 공간을 다섯 번 이상 확보하지 못할 때 이 예외가 발생한다.




    ■OOM에 대처하는 방법


    프로그램이 OOM으로 인해 종료될 때 자동으로 당시의 메모리 정보를


    파일(Heap Dump)로 남겨야 한다. 


    -XX:+HeapDumpOnOutOfMemoryError


    위 명령어를 프로그램 실행 옵션에 추가하면 OOM이 발생할 때


    JVM을 시작한 디렉토리에 Heap Dump 파일(.hprof)파일이 자동으로 생성된다.


    -XX:HeapDumpPath


    위 명령어는 Heap Dump 파일을 생성할 위치를 지정하는 역할을 한다.


    이 파일이 없다면 현상을 재현한 후 힙덤프 파일을 수동으로 만들어야 한다.


    먼저 Heap Dump 파일을 수동으로 생성하는 방법을 알아보고


    그 후에 생성된 Heap Dump 파일을 분석해본다.


    Heap Dump 파일 분석 방법으로는 크게 2가지가 있다.


    1. CLI 


    2. 이클립스 MAT(Eclipse Memory Analyzer)


    순서대로 확인해보자.




    ■Heap Dump란?


    Heap Dump : 특정 시점의 Heap 상태(정보)를 파일로 복사해 저장하는 작업.


    Memory Leak의 원인을 분석할 때 자주 사용된다.


    - Heap : 인스턴스가 저장되는 메모리 영역


    - Dump : 정보를 다른 곳으로 복사해 가서 저장하는 것 


    vi 등의 툴을 이용해서 Heap Dump 파일을 열면 안된다.


    여는 순산 파일의 힙 메모리 사이즈만큼 서버의 메모리를 사용하게 된다.


    그러므로 로컬 컴퓨터로 복사한 후 파일을 열어야 한다.



    ■Heap Dump 파일 생성 및 분석(CLI)


    OOM이 발생하는 클래스를 작성한다.


    import java.util.HashMap;
    import java.util.Map;

    public class HeapDumpTest {
    private Map<Integer, MemoryObject> leak = new HashMap<>();

    public static void main(String[] args) {
    HeapDumpTest heapDumpTest = new HeapDumpTest();
    heapDumpTest.run();
    }

    private void run() {
    for(int i = 0; ; i++) {
    // 해시맵에 저장만 하고 빼내지 않아 결국 OOM이 나는 구조.
    leak.put(i, new MemoryObject(i));
    System.out.println("leaking object " + leak.get(i).index);
    if( i == 5000 ) {
    try {
    // 데모를 위해 5000번 이후 sleep.
    System.out.println("Sleeping after adding " + i + "th element.");
    Thread.sleep(100000000L);
    } catch (final Exception e) {
    e.printStackTrace();
    }
    }
    }
    }

    class MemoryObject {
    int index ;
    MemoryObject(final int index) {
    this.index = index;
    }
    }
    }

    5001번째 MemoryObject가 leak에 저장되면 sleep 된다.

    leaking object 4996
    leaking object 4997
    leaking object 4998
    leaking object 4999
    leaking object 5000
    Sleeping after adding 5000 th element.

    Heap Dump 파일을 생성하기 위해 JDK 내의 jps 명령어를 통해 


    HeapDumpTest의 프로세스 번호(PID)를 찾는다.

    $ jps
    5408 Launcher
    7744 KotlinCompileDaemon
    14436 Jps
    10460 HeapDumpTest
    2348

    그리고 JDK 내의 jmap 명령어를 사용해서 Heap Dump 파일을 생성하자

    $ jmap -dump:live,file=./heapdump.hprof 10460
    Dumping heap to C:\study\JAVA-STUDY\heapdump.bin ...
    Heap dump file created

    - dump:live : 동작중인 인스턴스만 캡쳐


    - file : Dump 파일 저장 경로


    Dump파일을 확인한 후, jhat 명령어를 통해 파일을 실행시킨다.

    $ jhat -J-Xmx6g -port 7000 ./heapdump.hprof
    Reading from ./heapdump.hprof...
    Dump file created Tue Jul 07 07:32:16 KST 2020
    Snapshot read, resolving...
    Resolving 30042 objects...
    Chasing references, expect 6 dots......
    Eliminating duplicate references......
    Snapshot resolved.
    Started HTTP server on port 7000
    Server is ready.

    - Xmx : 실행시킬 프로그램의 메모리 사이즈 설정


    - 7000 : Dump 파일을 실행시킬 포트


    이제 브라우저를 통해 http://localhost:7000 주소로 접속해보자




    이 화면에서 모든 패키지들과 클래스들에 대한 정보를 확인할 수 있다.


    메모리 유출을 찾기 위해 "Show heap histogram" 링크를 클릭하자.



    이 표는 Total Size 내림차순으로 정렬되어있다.


    간단한 프로그램 하나를 돌렸을 뿐인데 수많은 인스턴스들이 생성되었음을 확인할 수 있다.


    위에서 7번째에 우리가 찾고자 하는 클래스 이름이 보인다. 


    확실히 5001개까지만 생성되었다.


    MemoryObject를 클릭하자.



    References to this object에 5001개의 MemoryObject 인스턴스들 리스트가 나온다.


    아무거나 한개 클릭해보자.

    이제 어느 레퍼런스가 이 인스턴스를 가지고 있는지 찾기 위해


    Exclude Weak refs를 클릭한다.


    이 화면을 통해 이 인스턴스가 어디에 속하는지 알 수 있다.


    이 인스턴스는 HeapDumpTest 클래스의 leak 이라는 필드에 연결되어 있다.


    또한 leak 필드는 HashMap의 인스턴스임을 알 수 있다.


    그리고 이 MemoryObject는 HashMap 인스턴스의 table 필드에 연결되어 있다.


    결국 leak이라는 HashMap 인스턴스가 MemoryObject 인스턴스를 가지고 있고


    leak이 Memory Leak의 원인임을 알 수 있다.





    ■Heap Dump 파일 생성 및 분석(이클립스 MAT)


    링크에서 이클립스 MAT을 다운받는 방법을 확인할 수 있다.


    생성된 Heap Dump 파일을 MAT으로 실행시키면 위와 같은 화면이 나타난다.

    아래에 몇 가지 용어가 등장한다.


    with incomming references : 해당 Object를 참조하는 Object 보여줌


    with outcomming references : 해당 Object가 참조하는 Object 보여줌


    Shallow Heap : 하나의 객체가 차지하는 영역


    Retained Heap : 해당 객체의 메모리와 해당 객체가 참조하는 객체들의 메모리를


    포함한 영역. Memory Leak을 분석할 때 특히 신경써야 할 영역


    Details



    힙에 대한 개략적인 정보를 제공한다.


    - Unreachable Objects Histogram


    클래스별로 생성된 객체에 대한 정보를 제공한다.


    Biggest Objects by Retained Size



    힙 영역에 생성된 Objects들 중, 가장 많은 영역을 차지하는 


    주요 Object의 비율을 파이차트로 표시한다.


    우측의 진한 파란색 영역을 우클릭한 후 


    List Objects -> with outgoing references를 클릭하면



    코드에서 생성했던 HashMap 노드가 약 5,000개 생성되어 있는 것을 볼 수 있다.



    Actions



    생성된 Object들에 대한 세부 내역을 보여준다.


    - Histogram


    힙 영역에 생성된 객체 타입에 따른 객체의 수를 표시.


    - Dominator Tree 


    생성된 객체에 대한 인과관계(생성관계)를 계층 형태로 표시하며


    생성된 객체의 크기 및 메모리 점유 %를 표시한다.


    아래 Reports의 LeakSuspect를 통해 현 메모리 상황에 대한 힌트를 얻은 후,


    Dominator Tree에서 자세하게 살펴보는 것이 효율적이다.



    코드에서 생성했던 HashMap 노드에 대한 자세한 정보를 확인할 수 있다.


    - Top Consumers


    가장 많이 생성된 객체를 중심으로 세보 정보 표시한다.


    - Duplicate Classes


    중복 생성된 클래스에 대한 정보를 표시한다.


    Reports



    힙 영역에 대한 분석 리포트를 제공한다.


    - LeakSuspect


    메모리 릭(Leak)으로 의심되는 객체에 대한 분석을 보여준다.



    코드에서 생성했던 HashMap 노드가 


    Problem Suspect 1로 선정되어 상단에 등장한다.


    - Top Conponents


    힙 영역에서 가장 많은 영역을 차지하는 객체에 대한 세부적이고 종합적인


    분석을 보여준다.



    Step By Step



    MAT을 이용하여 Heap Dump를 분석하는 절차 데모를 제공한다.




    출처


    https://gs.saro.me/dev?tn=460


    https://byplacebo.tistory.com/25


    https://imasoftwareengineer.tistory.com/4


    http://honeymon.io/tech/2019/05/30/java-memory-leak-analysis.html


    https://perfectacle.github.io/2019/04/28/heap-memory-analytics-with-eclipse-mat/


    https://spoqa.github.io/2012/02/06/eclipse-mat.html


    https://starplaying.tistory.com/430


    https://jupiny.com/2019/07/15/java-heap-dump-analysis/


    https://waspro.tistory.com/145


    https://m.blog.naver.com/2feelus/220780114184


    출처: https://byplacebo.tistory.com/21 [pLacebo]


    https://brunch.co.kr/@springboot/367


    https://byplacebo.tistory.com/21

    댓글

Designed by Tistory.