Haru Utils

JVM MEMORY

JVM OutOfMemoryError·OS OOM 장애

Java heap·Metaspace·direct/native memory와 Linux OOM kill을 구분하고 JVM flags, GC·class histogram, dump 공간과 민감도를 확인한 뒤 증거를 보존하며 최소 설정만 변경합니다.
Java heap spaceOutOfMemoryErrorMetaspace OOMDirect buffer memoryLinux OOMKilledjcmd GC.heap_infoHeapDumpOnOutOfMemoryError
환경Linux systemd · HotSpot/OpenJDK 17·21·25 · 서비스 계정으로 attach 가능한 JDK diagnostic tool
분류CPU·메모리
검토일2026-09-04
진행5단계 · 조회 우선

SAFE OPERATING BOUNDARY

중단·복구 기준부터 확인하세요

STOP CONDITIONS

여기서는 멈추세요

  • 서비스 계정·정확한 PID·같은 JDK major diagnostic tool을 확정하지 못했거나 현재 부하가 높은 경우 histogram·thread dump를 반복 실행하지 않습니다.
  • heap dump·thread dump·JFR 저장공간과 0600 권한·민감정보 처리 승인이 없으면 새 dump를 생성하지 않습니다.
  • DB migration·queue 처리 중인 서비스의 재시작과 retry·중복 처리 영향을 확인하지 못했다면 변경 단계로 이동하지 않습니다.
ROLLBACK

복구 기준

백업한 JVM option 파일과 이전 정상 code release를 같은 묶음으로 복원하고 unit을 검증한 뒤 한 번만 재시작합니다. heap dump·GC log·JFR은 원인 분석 전 삭제하지 않고 제한된 경로로 격리합니다. Xmx·MemoryMax 변경만 되돌려도 DB·queue 작업 중복이나 이미 손상된 in-memory state는 복원되지 않으므로 업무별 보정 절차를 따릅니다.

ESCALATION PACK

담당자에게 전달할 자료

  • 장애 시각의 Java OOM detail message·stack trace 요약과 kernel/cgroup OOM 기록
  • JVM version·VM.flags·GC.heap_info·RSS·MemoryMax·thread 수와 restart count
  • 권한 제한된 histogram·JFR·heap dump checksum과 변경 전후 GC·오류율·지연 지표
2026년 9월 4일 기준 공식 upstream 문서와 현재 지원 명령을 대조했습니다. 먼저 최소 범위 읽기 전용 조회로 사실을 확인하고, 변경은 영향·백업·복구 경로와 담당자 승인을 확보한 대상에만 적용합니다. <...> 자리표시자는 승인된 리터럴 값으로 직접 치환하며 외부 입력으로 shell 명령을 조립하거나 eval하지 않습니다.

BEFORE YOU START

이런 증상에서 시작합니다

  • 로그에 java.lang.OutOfMemoryError: Java heap space가 남음
  • Metaspace 또는 Direct buffer memory 오류 뒤 서비스가 재시작됨
  • JVM 오류 없이 journal kernel 로그에 Out of memory: Killed process가 남음
  • Full GC와 응답 지연이 반복되고 RSS가 계속 증가함

CHECK THE BRANCH

놓치기 쉬운 원인 분기

01

Java heap space·GC overhead limit인 경우

heap 최대값이 workload의 정상 peak보다 작은지, GC 후에도 특정 class instance가 누적되는지 구분합니다. Xmx 증가만으로 누수를 숨기지 않습니다.

02

Metaspace·classloader 관련인 경우

동적 class 생성과 반복 배포에서 classloader가 회수되지 않는지 확인합니다. heap histogram만으로 native Metaspace 크기를 단정하지 않습니다.

03

Direct buffer·native thread·unable to create native thread인 경우

heap 밖 native memory, thread 수, virtual memory와 process·cgroup 한계를 확인합니다. host available memory를 무시한 Xmx 증가는 native OOM을 악화시킵니다.

04

Linux OOM killer 또는 container limit인 경우

Java exception 없이 SIGKILL·exit 137이면 kernel/cgroup 로그와 MemoryMax·container limit을 우선 확인합니다. JVM heap dump가 생성되지 않을 수 있습니다.

FOLLOW THE FLOW

순서대로 확인하기

1
조회시스템을 변경하지 않는 확인 단계

장애 시각·종료 이유·kernel OOM 구분

서비스 상태와 restart count, Java 오류, kernel OOM 기록을 같은 시간축으로 확인합니다. 전체 로그를 외부로 복사하지 말고 필요한 오류 주변만 마스킹해 보존합니다.

서비스 상태·재시작
systemctl status <JAVA_SERVICE>.service --no-pager -l
systemctl show <JAVA_SERVICE>.service -p MainPID -p NRestarts -p ExecMainStatus -p Result
Java OOM 오류 주변
journalctl -u <JAVA_SERVICE>.service --since '<INCIDENT_START>' --until '<INCIDENT_END>' --no-pager | grep -E -C 5 'OutOfMemoryError|Java heap space|Metaspace|Direct buffer memory|unable to create native thread|Killed'
stack trace·요청값에는 개인정보와 내부 식별자가 포함될 수 있어 공유 전 마스킹합니다.
kernel·systemd OOM
journalctl -k --since '<INCIDENT_START>' --until '<INCIDENT_END>' --no-pager | grep -Ei -C 4 'out of memory|oom-kill|killed process'
cgroup memory 한계
systemctl show <JAVA_SERVICE>.service -p MemoryCurrent -p MemoryPeak -p MemoryMax -p OOMPolicy
결과 읽기

Java stack trace가 있으면 detail message로 memory 영역을 분류하고, kernel kill만 있으면 host/cgroup 압박을 우선 봅니다. 두 현상이 연쇄적으로 발생할 수도 있습니다.

다음 판단

현재 JVM이 살아 있다면 PID·flags·heap과 host memory 기준선을 수집합니다.

2
조회시스템을 변경하지 않는 확인 단계

JVM flags·heap·host memory 기준선 확인

jcmd는 대상과 같은 JDK major의 도구를 같은 Unix 계정으로 실행합니다. VM.flags·GC.heap_info는 비교적 낮은 영향이지만 과부하 중에는 한 번씩만 수집합니다.

Java process
ps -o pid,ppid,user,%cpu,%mem,rss,vsz,etime,stat,comm -p <JAVA_PID>
JVM flags
sudo -u <JAVA_SERVICE_USER> <MATCHING_JDK_HOME>/bin/jcmd <JAVA_PID> VM.flags
heap 정보
sudo -u <JAVA_SERVICE_USER> <MATCHING_JDK_HOME>/bin/jcmd <JAVA_PID> GC.heap_info
host memory·pressure
free -h
cat /proc/pressure/memory
ps -eo pid,user,rss,vsz,%mem,comm --sort=-rss | head -20
결과 읽기

Xmx와 실제 RSS 차이는 Metaspace·code cache·thread stack·direct buffer·native library·mapped file 등이 포함될 수 있습니다. RSS 하나만으로 leak을 확정하지 않습니다.

다음 판단

낮은 영향의 histogram·thread·GC 추세를 확보할지 장애 심각도와 pause 허용 범위로 판단합니다.

3
주의서버 부하나 권한을 고려할 단계

class·thread·GC 추세와 dump 가능성 확인

class histogram은 heap 크기에 따라 중간 이상의 pause·CPU 영향을 줄 수 있고 heap dump는 크고 민감합니다. 승인된 한 번의 histogram 또는 기존 dump·JFR을 우선 사용합니다.

class histogram 1회
sudo -u <JAVA_SERVICE_USER> <MATCHING_JDK_HOME>/bin/jcmd <JAVA_PID> GC.class_histogram
부하가 높거나 heap이 큰 운영 JVM에서는 성능 영향 승인을 받고 한 번만 실행합니다. 결과에는 업무 class 이름이 포함됩니다.
thread 수·상태
ps -L -p <JAVA_PID> -o pid,tid,psr,pcpu,stat,comm | head -200
sudo -u <JAVA_SERVICE_USER> <MATCHING_JDK_HOME>/bin/jcmd <JAVA_PID> Thread.print -l
thread dump에는 SQL·URL·사용자 데이터가 있을 수 있어 0600 경로에서 마스킹 후 공유합니다.
기존 JFR 상태
sudo -u <JAVA_SERVICE_USER> <MATCHING_JDK_HOME>/bin/jcmd <JAVA_PID> JFR.check
기존 dump·GC log와 공간
sudo find <APPROVED_JVM_DIAGNOSTIC_DIR> -maxdepth 1 -type f -printf '%m %u:%g %s %TY-%Tm-%TdT%TH:%TM:%TS %p\n'
df -hT <APPROVED_JVM_DIAGNOSTIC_DIR>
결과 읽기

시간을 두고 얻은 histogram·JFR에서 특정 class가 지속 성장할 때 leak 후보가 됩니다. 한 번의 상위 class 목록만으로 삭제·재시작·용량 증가를 결정하지 않습니다.

다음 판단

heap·native·thread·host 중 원인 영역과 dump 크기·민감도·쓰기 권한을 확정합니다.

4
조회시스템을 변경하지 않는 확인 단계

JVM 옵션·service limit·dump 경로 검토

현재 Java option의 관리 파일과 service override를 제한된 터미널에서 확인합니다. environment 원문이나 /proc/<PID>/environ은 secret을 포함할 수 있어 조회하지 않습니다.

unit·drop-in 경로
systemctl show <JAVA_SERVICE>.service -p FragmentPath -p DropInPaths -p User -p Group -p LimitNOFILE -p TasksMax -p MemoryMax
진단 경로 권한
sudo stat -Lc '%a %U:%G %n' <APPROVED_JVM_DIAGNOSTIC_DIR>
sudo -u <JAVA_SERVICE_USER> test -w <APPROVED_JVM_DIAGNOSTIC_DIR> && echo writable
disk 예상 여유
df -hT <APPROVED_JVM_DIAGNOSTIC_DIR>
sudo du -sh <APPROVED_JVM_DIAGNOSTIC_DIR>
서비스 로그 마지막 오류
journalctl -u <JAVA_SERVICE>.service -n 200 --no-pager
환경·요청·개인정보를 포함할 수 있어 원문을 외부 티켓에 붙여넣지 않습니다.
결과 읽기

HeapDumpPath는 최소 Xmx에 가까운 파일과 임시 쓰기 여유를 감당해야 하며 서비스 계정만 쓸 수 있어야 합니다. host memory는 Xmx보다 충분히 커야 합니다.

다음 판단

원인에 맞는 코드·limit·JVM 옵션 중 하나의 최소 변경과 되돌릴 파일을 승인합니다.

5
변경데이터·서비스 상태가 달라질 수 있는 단계

진단 보강 또는 용량·코드 수정 후 검증

이 단계는 JVM 재시작을 수반합니다. 변경 파일과 현재 상태를 0600으로 백업하고, heap·Metaspace·thread·host 원인에 맞는 한 가지 수정만 적용합니다.

변경 단계입니다. 실행 전 대상 이름과 경로, 서비스 중단 영향, 복구 방법을 다시 확인하세요.
JVM 옵션 파일 백업·편집
install -d -m 0700 <APPROVED_BACKUP_DIR>
umask 077
sudo cp --archive <JVM_OPTIONS_FILE> <APPROVED_BACKUP_DIR>/jvm-options.before
sudo chmod 0600 <APPROVED_BACKUP_DIR>/jvm-options.before
sudoedit <JVM_OPTIONS_FILE>
예: -XX:+HeapDumpOnOutOfMemoryError와 -XX:HeapDumpPath=<APPROVED_JVM_DIAGNOSTIC_DIR>를 검토합니다. Xmx는 host·cgroup·native 여유를 계산한 승인값만 사용합니다.
옵션·unit 검증
sudo -u <JAVA_SERVICE_USER> <MATCHING_JDK_HOME>/bin/java -version
sudo systemd-analyze verify <JAVA_SERVICE_UNIT_FILE>
JVM binary와 unit 문법만 확인하며 애플리케이션을 별도로 시작하지 않습니다.
승인된 한 번의 재시작
sudo systemctl restart <JAVA_SERVICE>.service
systemctl status <JAVA_SERVICE>.service --no-pager -l
새 PID·flags·health
systemctl show <JAVA_SERVICE>.service -p MainPID -p NRestarts -p MemoryCurrent -p MemoryPeak
sudo -u <JAVA_SERVICE_USER> <MATCHING_JDK_HOME>/bin/jcmd <NEW_JAVA_PID> VM.flags
curl -fsS http://127.0.0.1:<APP_PORT>/<HEALTH_PATH>
결과 읽기

서비스 health·오류율이 회복되고 RSS·GC pause·allocation rate·restart count가 정상 범위여야 합니다. 옵션이 실제 flags에 반영됐는지 확인합니다.

다음 판단

악화되면 백업한 JVM option 파일과 이전 code release를 복원해 한 번만 재시작합니다. dump·JFR·로그에는 고객 데이터가 있을 수 있어 0600·암호화·보존 기간을 적용하고, 원인은 heap dump 분석·allocation profile·code change로 제거합니다.

PRIMARY REFERENCES

공식 문서

배포판과 버전에 따라 옵션·로그 위치가 다를 수 있습니다. 실행 전 서버의 --help와 로컬 매뉴얼을 함께 확인하세요.

도구 빠른 검색

최근 사용한 도구를 다시 열거나, 이름과 기능으로 검색하세요.

검색어와 도구의 입력·결과는 저장하지 않습니다.

↑↓ 이동 · Enter 열기 · Esc 닫기