1. Core Dump
코어 덤프(core dump) = 메모리 덤프(memory dump) = 시스템 덤프(system dump)
코어 덤프(core dump)는 컴퓨터 프로그램이 특정 시점에 작성 중이던 메모리 상태를 기록한 것으로, 보통 프로그램이 비정상적으로 종료했을 때 만들어진다. 실제로는 그 외에 중요한 프로그램 상태도 같이 기록되곤 하는데, 프로그램 카운터, 스택 포인터 등 CPU 레지스터나, 메모리 관리 정보, 그 외 프로세서 및 운영 체제 플래그 및 정보 등이 포함된다. 코어 덤프는 프로그램 오류 진단과 디버깅에 쓰인다.
- 리눅스에서 gcore라는 명령어를 사용해서 코어 덤프를 남길 수 있다.
- JVM은 hs_err_pid.log라는 파일을 남기고 죽는다.
- java에서 1GB의 메모리를 사용하면 코어 덤프는 수십기가에 달하는 파일을 생성한다.
- 아무 근거 없이 죽었다면 kill -9 <pid>로 죽었거나 segfault와 같이 프로세스 내의 오류로 죽었을 가능성이 있다.
-> var/log messages 로그를 봐야한다.
- 코어 덤프 자동으로 생성하게 만들기
- ulimit -a 명령어로 서버 설정을 확인한다.
- core file size(blocks, -c) 0 <- 이처럼 0이면 core dump는 안남는다.
- ulimit -c unlimited <-core dump를 남기도록 변경한다.
- 코어 덤프 분석하기
- gdb 프로그램을 사용한다.
- gdb/자바실행파일 Full path/java core.pid <- 실행
- 인터프리터 방식으로 이 툴을 사용할 수 있다.
- 명령어
- bt
- info thread
- thread 쓰레드번호
- where
- x/i 메모리 주소값
2. Heap Dump
Heap Dump는 현재 Heap에서 점유되고 있는 객체들에 대한 조사를 위해 필요하다.
- JVM GC 블로킹에서 Old 영역을 많이 사용할 경우 일반적으로 사용되지 않는 객체들이 Reference되어 GC되지 않고 남아 있을 가능성이 높다. 이때는 어떤 객체들이 많이 점유되고 있는지 조사할 필요가 있다.
- Old 영역을 많이 점유하고 있으면 Full GC가 자주 오래동안 발생할 수 있으므로 업무 응답시간에 문제 또는 장애를 일으킬 소지가 있다.
1. Heap Dump on OutOfMemoryError
- Java 시작 시에 다음과 같은 옵션을 주면, OutOfMemoryError로 이한 JVM에 종료시 Heap Dump를 생성해준다.
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=jvm.hprof - -XX:HeapDumpPath를 생략하면 JVM 시작 디렉토리에 java_pid<pid>.hprof 형태로 생성된다.
- -XX:+PrintClassHistogramAfterFullGC, -XX:+PrintClassHistogramBeforeFullGC 등의 옵션으로 Full GC 전/후의 메모리 상태를 간략히 덤프할 수 있다.
2. jmap
- 힘 덤프(Heap Dump)를 뜬다.
- WebServer - Heap Dump를 뜨는 작업은 매우 큰 용량과 시간을 필요로 하기 때문에 WebServer - WAS 구조일 때 WebServer를 내려서 WAS에 요청이 오지 않게 만든 뒤에 작업해야 한다.
- live 옵션을 주면 live 객체만 덤프를 받는다. 이 옵션이 없으면 Heap 상의 모든 객체를 덤프 받는다.
- 바이터리 파일로 덤프 받기
# jmap 사용 jps # Java PID 확인 jmap -dump:live,format=b,file=파일명.bin <pid> # jmap 잘 안될 때 force. live 허용 안됨 jmap -F -dump:format=b,file=파일명.bin <pid> # gcore 사용 gcore -o 파일명.core <pid> # 파일명.core.<pid>파일생성 jmap -dump:format=b,file=파일명.bin /usr/java/defaults/bin/java 파일명.core
- 한번에 확인해보기
jps -v | grep "원하는 검색어" | awk '{print $1}' | xargs jmap -dump:live,format=b,file=jdump.hprof
- jstatd를 띄웠을 경우, <pid> 대신 <pid>@호스트네임:포트 형태로 호출해도 된다.
- JVM 현재 메모리 상태 Foot print. 현재 메모리 상의 클래스 객체 개수와 용량 표시
jmap -histo:live <PID>
3. jhat
- 힘 덤프를 분석한다.
- jhat -J-mx2048m 파일명.bin 실행 후 http://localhost:7000/에서 살펴볼 수 있다.
- 힘 덤프 파일의 크기가 클 경우 OOM 에러가 발생할 수 있으므로 -J-mx2048 지정 - 메모리를 너무 많이 먹어서 실제로 제대로 실행하기 힘듦
4. MAT
- Java Memory Analyzer(MAT)
- Shallow heap : 하나의 객체가 소비하는 메모리 용량. 객체 레퍼런스는 하당 아키텍처에 따라 32bits(혹은 62bits)를 차지한다.
- Retained heap : 해당 객체의 모든 인스턴스를 GC했을 때 확보할 수 있는 메모리 총량
5. IBM HeapAnalyzer
- 힘덤프 파일의 크기가 크기 때문에 실행시 -Xmx2048m 형태로 메모리 옵션을 줘야 한다.
6. HPJmeter
- GC Log, Heapdump 등을 분석할 수 있다.
7. jstat
- 현재 JVM의 메모리 상태를 확인해볼 수 있다.
- gc 예
jps # PID 확인 jstat -gc <PID> 1000 # 1초마다 gc 확인
jstat options
옵션명 | 내용 |
class | 클래스 로더의 동작에 관한 통계 데이터 |
compiler | HotSpot Jus-In-Time 컴파일러의 동작에 관한 통계 데이터 |
gc | GC된 heap의 동작에 관한 통계 데이터 |
gccapacity | 세대마다 용량과 대응하는 영역에 관한 통계 데이터 |
gcnew | New 세대의 동작에 관한 통계 데이터 |
gcnewcapacity | New 세대의 사이즈와 대응하는 영역에 관한 통계 데이터 |
gcold | Old 세대 및 Permanent 세대의 동작에 관한 통계 데이터 |
gcoldcapacity | Old 세대의 사이즈에 관한 통계 데이터 |
gcpermcapacity | permanent 세대의 사이즈에 관한 통계 데이터 |
gcutil | GC 통계 데이터의 개요 |
printcompilation | HotSpot 컴파일 방법의 통계 데이터 |
SSH로 특정 서버의 GC 상태 보기
서버가 여러대일 경우 특정 서버에 자동 접근하여 해당 서버의 Tomcat 인스턴스를 찾아서 gcutil 실행
ssh myhostname 'bash -s' << 'ENDSSH' jstat -gcutil `jps | grep Bootstrap | awk '{print $1}'` 1s ENDSSH
Code로 heap dump 뜨기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
import javax.management.MBeanServer; import java.lang.management.ManagementFactory; import com.sun.management.HotSpotDiagnosticMXBean; /** * 현재 Java Application의 Heap Dump를 뜨는 함수 * @see https://blogs.oracle.com/sundararajan/programmatically-dumping-heap-from-java-applications">Programmatically dumping heap from Java applications</a href=" */ public class HeapDumper { // This is the name of the HotSpot Diagnostic MBean private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic"; // field to store the hotspot diagnostic MBean private static volatile HotSpotDiagnosticMXBean hotspotMBean; /** * Call this method from your application whenever you * want to dump the heap snapshot into a file. * * @param fileName name of the heap dump file * @param live flag that tells whether to dump * only the live objects */ public static void dumpHeap(String fileName, boolean live) { // initialize hotspot diagnostic MBean initHotspotMBean(); try { hotspotMBean.dumpHeap(fileName, live); } catch (RuntimeException re) { throw re; } catch (Exception exp) { throw new RuntimeException(exp); } } // initialize the hotspot diagnostic MBean field private static void initHotspotMBean() { if (hotspotMBean == null) { synchronized (HeapDumper.class) { if (hotspotMBean == null) { hotspotMBean = getHotspotMBean(); } } } } // get the hotspot diagnostic MBean from the // platform MBean server private static HotSpotDiagnosticMXBean getHotspotMBean() { try { MBeanServer server = ManagementFactory.getPlatformMBeanServer(); HotSpotDiagnosticMXBean bean = ManagementFactory.newPlatformMXBeanProxy(server, HOTSPOT_BEAN_NAME, HotSpotDiagnosticMXBean.class); return bean; } catch (RuntimeException re) { throw re; } catch (Exception exp) { throw new RuntimeException(exp); } } public static void main(String[] args) { // default heap dump file name String fileName = "heap.hprof"; // by default dump only the live objects boolean live = true; // simple command line options switch (args.length) { case 2: live = args[1].equals("true"); case 1: fileName = args[0]; } // dump the heap dumpHeap(fileName, live); } } Colored by Color Scripter |
cs |
3. Thread Dump
Thread Dump는 현재 수행중인 쓰레드에 대한 호출 경로 StackTrace를 보기 위해 필요하다.
쓰레드 덤프로 확인할 수 있는 상황
- 모든 시스템에 응답이 없을 때
- 사용자 수가 많지 않은데 cpu 사용량이 높을 때
- 특정 어플리케이션 수행 시 응답이 없을 때
- 서비스 실행시간이 길어질 수록 응답시간이나 CPU 사용량이 늘어날 때
- java.lang.Thread를 이용해서 직접 개발하지 않았는데 이미 프레임워크, WAS, 라이브러리를 사용하고 있을 때 내부적으로 문제가 생길 때
- 명령어를 통해 프로세스의 THREAD 단위로 CPU 사용률을 모니터링하여 CPU율을 모니터링하여 CPU를 많이 차지하는 THREAD 확인
$ prstat -L -m -p [프로세스ID] [Intervall]
가장 높은 cpu를 차지하는 thread의 twpid를 16진수로 변환(=threadDump의 nid)
또는 아래의 명령어 사용 가능
$ ps -mo pid,lwd,stime,time,cpu -C java
가장 높은 cpu를 차지하는 thread의 TID를 16진수로 변환(=thread Dump의 native ID)
- THREAD DUMP 생성
1) pid 확인
$ ps -eF | grep java $ jps -mlv
혹은
$ sudp ps -aux | grep java
2) JCMD 이용
$ cd /원하는 폴더 $ jcmd ${pid} Thread.print > #{fileName}.print
3) kill -3 이용
$ kill -3 pid
4) JSTACK 이용
$ jstack <pid> <filename> $ jstack -l <pid> > <file-path>
JSTACK 스크립트
#!/usr/bin/env bash date_postfix=$(date +"%Y-%m-%dT%H:%M:%S.txt") file_name=$date_postfix top_process_info=$(ps aux --sort=-pcpu | head -n 2 | grep -v PID) i=0 user='null' pid='null' cmd='' for token in $top_process_info do if [ $i == 0 ]; then user=$token fi if [ $i == 1 ]; then pid=$token fi if [ $i -ge 10 ]; then cmd=$cmd' '$token fi i=$(($i + 1)) done top_thread_info=$(top -H -b -n1 -p "$pid" | head -n 8 | tail -n 1 | grep -v PID) i=0 tid='null' pcpu='null' for token in $top_thread_info do if [ $i == 0 ]; then tid=$token fi if [ $i == 8 ]; then pcpu=$token fi i=$(($i + 1)) done tid_hex=$(printf 'nid=0x%x\n' $tid) thread_dump=$(sudo -u "$user" -H sh -c "/${JAVA_PATH}/bin/jstack -l $pid" | grep $tid_hex -A 100 ) echo "[command] $cmd" >> "$file_name" echo "[top_thread] $tid" >> "$file_name" echo "[top_thread_hex] $tid_hex" >> "$file_name" echo "[top_thread_pcpu] $pcpu" >> "$file_name" echo "$thread_dump" >> "$file_name" date_postfix=$(date +"_%Y-%m-%dT%H:%M:%S.txt") ps_file_name='ps'$date_postfix top_file_name='top'$date_postfix td_file_name='td'$date_postfix top_process_id=$(ps aux --sort=-pcpu | head -n 2 | grep -v PID | awk '{print $2}') user=$(ps -p "$top_process_id" -o user | grep -v USER) ps_info_columns='UID PID PPID LWP C NLWP STIME TTY TIME CMD' ps_info=$(ps -eLf --sort=-pcpu | grep "$top_process_id") top_info=$(top -H -b -n1 -p "$top_process_id") echo "$ps_info_columns" >> "$ps_file_name" echo "$ps_info" >> "$ps_file_name" echo "$top_info_columns" >> "$top_file_name" echo "$top_info" >> "$top_file_name" top_dump=$(sudo -u "$user" -H sh -c "/${JAVA_PATH}/bin/jstack -l $top_process_id") echo "$top_dump" >> "$td_file_name"
4. Heap Dump / Thread Dump 분석 툴
- fastThread
- 덤프 파일을 올리면 분석해줌
- IntelliJ IDEA
- Ultimate 버전에서만 가능
- THREADLOGIC.JAR
- VisualVM
- JDK에 기본으로 들어있음
- MAT(Memory Analyzer)
- IBM HeapAnalyzer
- GCEASY
참고
- https://yenaworldblog.wordpress.com/2018/05/09/thread-dump-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EB%B6%84%EC%84%9D-%EB%B0%A9%EB%B2%95/
- https://kwonnam.pe.kr/wiki/java/memory
- 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://jupiny.com/2019/07/15/java-heap-dump-analysis/
- http://iloveulhj.github.io/posts/java/threaddump,heapdump.html
- https://n1tjrgns.tistory.com/243
- https://cognitive.tistory.com/?page=6
- https://ko.wikipedia.org/wiki/%EC%BD%94%EC%96%B4_%EB%8D%A4%ED%94%84
- https://d2.naver.com/helloworld/10963
- https://blog.leocat.kr/notes/2016/03/07/java-dump-analyze-tool
'Cloud + System > IT인프라 기본개념' 카테고리의 다른 글
[LDAP] 개념 잡기 (0) | 2021.11.07 |
---|---|
SSO란? (0) | 2021.10.31 |
2. 가상화, 전가상화, 반가상화 (0) | 2021.10.24 |
1.가상화 (0) | 2021.10.24 |
IT인프라 기본용어 (0) | 2021.10.23 |