Java 에서 OOM 발생한 프로세스 디버깅 방법

Java에서 OutOfMemoryError(OOM) 발생한 프로세스 디버깅 방법

OOM이 발생하면 애플리케이션이 비정상적으로 종료되거나, 성능 저하가 발생할 수 있습니다.
이를 해결하기 위해 Heap Dump 분석, GC 로그 확인, 스레드 덤프 분석 등의 방법을 사용하여 원인을 찾을 수 있습니다.


1. OOM 유형 확인

OOM 오류가 발생하면 로그를 먼저 확인하여 어떤 유형인지 파악해야 합니다.

(1) Java Heap Space (힙 메모리 부족)

java.lang.OutOfMemoryError: Java heap space
  • 객체를 너무 많이 생성하여 Heap 메모리가 부족한 경우
  • 원인 예시:
    • 대량의 컬렉션 (List, Map)에 데이터 저장
    • 캐시(Ehcache, Guava Cache) 오버로드
    • 대용량 파일을 한 번에 읽기

(2) GC Overhead Limit Exceeded

java.lang.OutOfMemoryError: GC overhead limit exceeded
  • GC가 과도하게 실행되며, 애플리케이션이 정상적으로 실행되지 않는 경우
  • 원인 예시:
    • 너무 작은 Heap 설정
    • 객체가 지속적으로 생성되지만 해제되지 않는 경우 (메모리 누수)

(3) Metaspace OOM (JDK 8 이상)

java.lang.OutOfMemoryError: Metaspace
  • 클래스 로딩이 과도하게 발생하여 Metaspace 메모리가 부족한 경우
  • 원인 예시:
    • 동적 클래스 로딩 (Reflection, JPA, Groovy)
    • 스프링 애플리케이션의 Bean 개수가 너무 많음

(4) Direct Buffer Memory 부족

java.lang.OutOfMemoryError: Direct buffer memory
  • NIO ByteBuffer가 과도하게 사용되었을 때 발생
  • 원인 예시:
    • Netty, Tomcat, Kafka 등에서 네트워크 버퍼를 과도하게 사용

2. Heap Dump 생성 및 분석

OOM이 발생하면 Heap Dump를 생성하여 어떤 객체가 메모리를 과도하게 점유하는지 분석해야 합니다.

(1) OOM 발생 시 자동 Heap Dump 저장

Tomcat 또는 Spring Boot 실행 시 아래 옵션을 추가하면 OOM 발생 시 Heap Dump가 자동으로 저장됩니다.

JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof"

(2) 수동으로 Heap Dump 생성

jmap -dump:live,format=b,file=/tmp/heapdump.hprof <PID>
  • jps 명령어로 PID(프로세스 ID) 확인 가능
jps -l

(3) Heap Dump 분석 도구

Heap Dump 파일 (heapdump.hprof)을 분석하려면 다음 도구를 사용할 수 있습니다.

  • Eclipse MAT (Memory Analyzer Tool) → 가장 많이 사용됨
  • JVisualVM
  • YourKit / IntelliJ Profiler

MAT로 분석할 때 주요 지표

  • Histogram: 객체가 얼마나 많이 생성되었는지 확인
  • Dominator Tree: 가장 메모리를 많이 사용하는 객체 찾기
  • Leak Suspects Report: 메모리 누수 가능성이 높은 객체 탐색

3. GC 로그 확인

OOM이 발생하기 전에 GC가 과도하게 발생했는지 확인해야 합니다.

(1) GC 로그 활성화

애플리케이션 실행 시 아래 옵션을 추가합니다. (JDK 8 이상)

JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/tmp/gc.log"

(2) 실시간 GC 확인

jstat -gc <PID> 1000

출력 예시:

 S0C    S1C    E    O    M    CCSC   YGC    FGC
 51200  51200  102400  512000  128000  32000  5  1
  • E: Eden 영역 (Young GC)
  • O: Old 영역 (Full GC)
  • YGC: Young GC 발생 횟수
  • FGC: Full GC 발생 횟수 (너무 많으면 메모리 문제 가능성 높음)

4. 스레드 덤프 확인 (Deadlock, Thread Leak)

OOM과 함께 Deadlock 또는 Thread Leak이 발생하는 경우도 있음.

(1) 스레드 덤프 생성

jstack <PID> > thread_dump.txt

(2) 스레드 덤프 분석

  • 특정 스레드가 Blocked 상태로 대기 중인지 확인
  • CPU를 과도하게 사용하는 스레드 확인

5. 메모리 문제 해결 방법

(1) JVM 메모리 설정 조정

OOM이 발생하면 JVM의 Heap 크기를 늘려볼 수 있습니다.

JAVA_OPTS="$JAVA_OPTS -Xms512m -Xmx2g"

하지만, 무조건 메모리를 늘리는 것은 근본적인 해결책이 아님 → 반드시 메모리 누수를 먼저 해결해야 함.


(2) 메모리 누수(Large Object) 점검

아래와 같은 메모리 누수 패턴을 확인해야 합니다.

1) 대량의 데이터 컬렉션 사용

private static final List<String> cache = new ArrayList<>();

🔴 해결 방법: 필요 없는 객체를 제거하거나 WeakReference, SoftReference 활용

2) Static 변수에 대량의 데이터 저장

public static List<User> users = new ArrayList<>();

🔴 해결 방법: 객체를 적절히 해제 (clear())

3) Listener, ThreadPool 미해제

context.addListener(new CustomListener());

🔴 해결 방법: context.removeListener() 사용하여 리소스 해제


(3) GC 튜닝 및 관리

OOM을 방지하기 위해 적절한 GC 정책을 선택해야 합니다.

JVM GC 설정 (G1GC)

JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
  • G1GC: 대량의 데이터 처리 시 효율적
  • MaxGCPauseMillis=200: GC로 인해 발생하는 지연 최소화

JVM GC 설정 (ZGC)

(JDK 11 이상에서 사용 가능)

JAVA_OPTS="$JAVA_OPTS -XX:+UseZGC"
  • 저지연 GC, 대량의 메모리 사용 애플리케이션에 적합

6. 결론

  1. OOM 로그 확인 (Java Heap Space, GC Overhead, Metaspace, Direct Buffer)
  2. Heap Dump 생성 (jmap) → MAT로 분석
  3. GC 로그 확인 (jstat, gc.log)
  4. 스레드 덤프 확인 (jstack)
  5. JVM 메모리 설정 조정 (Xmx, GC 옵션)
  6. 메모리 누수 원인 분석 및 최적화

이 과정을 수행하면 Java OOM 발생 원인을 정확하게 찾고 해결할 수 있습니다. 🚀

profile
AI 답변 글을 주로 올립니다.

0개의 댓글