-
Java Heap DumpJava/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://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
'Java > Basics' 카테고리의 다른 글
같은 value 값을 가지는 String들의 hashcode가 같은 이유 (0) 2020.06.19 메모리 주소만으로 Heap의 인스턴스에 접근할 수 있을까? (2) 2020.06.17 JVM, JRE, JDK (0) 2020.06.14 Java Heap (with GC) (0) 2020.06.08 자바 컨벤션 모음 (0) 2020.05.21 댓글