LabHub
배우기 러닝패스 코스

힙은 남았는데 서비스가 멈췄다 · 힙 덤프에서 범인을 찾는다 · 실습

힙 덤프에서 범인을 찾는다

LabHub 에서 이어서 보기

목표

OutOfMemoryError 를 직접 만들고, 힙 덤프와 클래스 히스토그램으로 범인을 찾고, 재발을 막는 기동 옵션을 스크립트로 남긴다.

왜 중요한가

덤프는 사건의 순간에만 만들어진다. 옵션을 켜 두지 않으면 사건 뒤에는 아무것도 없다. 그리고 OutOfMemoryError 는 프로세스를 죽이지 않는다 — 반쯤 죽은 채 헬스체크를 통과하는 상태를 ExitOnOutOfMemoryError 가 끝낸다. 재료 Leak.java-Dleak.kb=256 크기 배열을 정적 맵에 계속 넣고(-Dleak.retain=false 면 넣지 않음), -Dleak.sleep.ms 로 속도를, -Dleak.iterations 로 횟수를 정한다. 시작할 때 max_heap_mb= 를 찍는다.

단계

1. /root/jvm/heap 에 Leak.java 를 컴파일하고 java -Xmx64m -cp /root/jvm/heap Leak > /root/jvm/heap/oom.log 2>&1 로 돌린다. 로그에 java.lang.OutOfMemoryError: Java heap space 가 있어야 한다.
2. -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/root/jvm/heap/oom.hprof 를 붙여 다시 돌리고 출력을 /root/jvm/heap/oom-dump.log 에 남긴다. oom.hprof 가 생기고(1MB 이상, JAVA PROFILE 1.0.2 로 시작) 로그에 Heap dump file created 가 있어야 한다.
3. java -Xmx64m -Dleak.sleep.ms=50 -cp /root/jvm/heap Leak 을 백그라운드로 띄우고 2초 뒤 jcmd <pid> GC.class_histogram > /root/jvm/heap/histo.txtjcmd <pid> GC.heap_dump /root/jvm/heap/live.hprof 를 남긴다(그 뒤 프로세스는 kill 해도 된다).
4. histo.txt 의 1위 줄에서 클래스 이름과 인스턴스 수를 읽어 /root/jvm/heap/top.txttop_class=<이름>instances=<n> 두 줄로 적는다.
5. java -Xmx64m -Dleak.retain=false -Dleak.iterations=3000 -cp /root/jvm/heap Leak > /root/jvm/heap/noleak.log 2>&1 로 같은 할당을 참조 없이 돌린다. 로그에 done iterations=3000 이 있고 OutOfMemoryError 가 없어야 한다.
6. -XX:+ExitOnOutOfMemoryError 를 붙여 돌리고 출력을 /root/jvm/heap/exit-on-oom.log 에, 종료 코드(echo $?)를 /root/jvm/heap/exit.txt 에 남긴다. 로그에 Terminating due to java.lang.OutOfMemoryError 가 있고 exit.txt 는 3 이어야 한다.
7. java -XX:+PrintFlagsFinal -versionMaxHeapSize 값과 -XX:MaxRAMPercentage=50 을 더한 값을 읽어 /root/jvm/heap/ram.txtdefault_max_heap=<바이트>half_max_heap=<바이트> 두 줄로 적는다.
8. /root/jvm/heap/start.sh 를 만든다: -Xmx64m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/root/jvm/heap/dumps -XX:+ExitOnOutOfMemoryError -Xlog:gc:file=/root/jvm/heap/gc.log 로 Leak 을 돌리고 종료 코드를 /root/jvm/heap/runbook-exit.txt 에 적는 스크립트다. 실행하면 /root/jvm/heap/dumps/ 에 .hprof 가 생기고 gc.log 가 생기며 runbook-exit.txt 가 3 이어야 한다.

참고

단계 8개

  1. OutOfMemoryError 를 만든다
  2. 사건의 순간에 덤프를
  3. 살아 있는 프로세스의 히스토그램과 덤프
  4. 히스토그램 첫 줄을 읽는다
  5. 같은 할당, 참조만 없으면
  6. 좀비를 만들지 않는다
  7. 컨테이너 힙 기본값을 잰다
  8. 옵션을 스크립트에 못 박는다