장애 대응 (jstack·jmap·OOM killer·FD)
3. 코드 예제
예제 1: 스레드 덤프 읽기 — 01_incident.sh 발췌
jstack "$PID" > td1.txt
grep -c '^"' td1.txt
grep -A2 '"busy-worker"' td1.txt | head -n 3
grep -A2 '"lock-waiter"' td1.txt | head -n 3
grep -E '^\s+- (waiting to lock|locked)' td1.txt | sort | uniq -c스레드 수: 21
"busy-worker" #21 daemon prio=5 cpu=4484.38ms elapsed=6.59s nid=38040 runnable
java.lang.Thread.State: RUNNABLE
at Incident.lambda$main$0(Incident.java:12)
"lock-waiter" #23 daemon prio=5 cpu=0.00ms elapsed=6.38s nid=43052 waiting for monitor entry
java.lang.Thread.State: BLOCKED (on object monitor)
at Incident.lambda$main$2(Incident.java:23)
1 - locked <0x00000000ffc57c30> (a java.lang.Object)
1 - waiting to lock <0x00000000ffc57c30> (a java.lang.Object)cpu=4484ms 가 6.6초 동안 4.5초를 썼다는 뜻이라 CPU 범인이 바로 보입니다. Incident.java:12 가 그 코드 줄입니다. 같은 주소를 잡은 쪽과 기다리는 쪽이 잠금 관계입니다.
예제 2: 상태별 집계 — 01_incident.sh 발췌
grep -oE 'java.lang.Thread.State: [A-Z_]+' td1.txt | sort | uniq -c | sort -rn 9 java.lang.Thread.State: RUNNABLE
3 java.lang.Thread.State: TIMED_WAITING
1 java.lang.Thread.State: WAITING
1 java.lang.Thread.State: BLOCKED02 레슨의 빈도 패턴을 덤프에 적용한 것입니다. 운영 서버에서 BLOCKED 가 두 자리면 잠금, RUNNABLE 이 스레드 풀 크기와 같으면 풀 고갈입니다.
예제 3: 힙 상태와 히스토그램 — 01_incident.sh 발췌
jcmd "$PID" GC.heap_info | head -n 3
jstat -gcutil "$PID" 1000 2 | tail -n 2
jmap -histo:live "$PID" | head -n 6 garbage-first heap total 260096K, used 105390K
region size 1024K, 3 young (3072K), 0 survivors (0K)
- - 8.70 43.29 53.07 10.80 0 0.000 0 0.000
num #instances #bytes class name (module)
1: 2829 52588720 [B ([email protected])
2: 138 160824 [C ([email protected])[B 가 52MB 로 1위이고 데모 앱이 byte[1MB] 50개를 리스트에 넣은 것과 맞습니다. 실제 앱에서는 이 자리에 누수 클래스가 옵니다.
예제 4: 힙 덤프 파일 — 01_incident.sh 발췌
jcmd "$PID" GC.heap_dump "$WORK/heap.hprof"
ls -l "$WORK/heap.hprof"Heap dump file created [55721329 bytes in 0.103 secs]
55721329 bytes힙 사용량 105MB 중 살아 있는 객체만 55MB 로 기록됐습니다. 운영 힙 4GB 면 파일도 수 GB 이므로 디스크 여유를 먼저 확인합니다.