OOM이 발생하면 애플리케이션이 비정상적으로 종료되거나, 성능 저하가 발생할 수 있습니다.
이를 해결하기 위해 Heap Dump 분석, GC 로그 확인, 스레드 덤프 분석 등의 방법을 사용하여 원인을 찾을 수 있습니다.
OOM 오류가 발생하면 로그를 먼저 확인하여 어떤 유형인지 파악해야 합니다.
java.lang.OutOfMemoryError: Java heap space
List, Map)에 데이터 저장 Ehcache, Guava Cache) 오버로드 java.lang.OutOfMemoryError: GC overhead limit exceeded
java.lang.OutOfMemoryError: Metaspace
Reflection, JPA, Groovy) java.lang.OutOfMemoryError: Direct buffer memory
OOM이 발생하면 Heap Dump를 생성하여 어떤 객체가 메모리를 과도하게 점유하는지 분석해야 합니다.
Tomcat 또는 Spring Boot 실행 시 아래 옵션을 추가하면 OOM 발생 시 Heap Dump가 자동으로 저장됩니다.
JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof"
jmap -dump:live,format=b,file=/tmp/heapdump.hprof <PID>
jps 명령어로 PID(프로세스 ID) 확인 가능jps -l
Heap Dump 파일 (heapdump.hprof)을 분석하려면 다음 도구를 사용할 수 있습니다.
OOM이 발생하기 전에 GC가 과도하게 발생했는지 확인해야 합니다.
애플리케이션 실행 시 아래 옵션을 추가합니다. (JDK 8 이상)
JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/tmp/gc.log"
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 발생 횟수 (너무 많으면 메모리 문제 가능성 높음)OOM과 함께 Deadlock 또는 Thread Leak이 발생하는 경우도 있음.
jstack <PID> > thread_dump.txt
OOM이 발생하면 JVM의 Heap 크기를 늘려볼 수 있습니다.
JAVA_OPTS="$JAVA_OPTS -Xms512m -Xmx2g"
하지만, 무조건 메모리를 늘리는 것은 근본적인 해결책이 아님 → 반드시 메모리 누수를 먼저 해결해야 함.
아래와 같은 메모리 누수 패턴을 확인해야 합니다.
private static final List<String> cache = new ArrayList<>();
🔴 해결 방법: 필요 없는 객체를 제거하거나 WeakReference, SoftReference 활용
public static List<User> users = new ArrayList<>();
🔴 해결 방법: 객체를 적절히 해제 (clear())
context.addListener(new CustomListener());
🔴 해결 방법: context.removeListener() 사용하여 리소스 해제
OOM을 방지하기 위해 적절한 GC 정책을 선택해야 합니다.
JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
G1GC: 대량의 데이터 처리 시 효율적MaxGCPauseMillis=200: GC로 인해 발생하는 지연 최소화(JDK 11 이상에서 사용 가능)
JAVA_OPTS="$JAVA_OPTS -XX:+UseZGC"
Java Heap Space, GC Overhead, Metaspace, Direct Buffer)jmap) → MAT로 분석jstat, gc.log)jstack)Xmx, GC 옵션)이 과정을 수행하면 Java OOM 발생 원인을 정확하게 찾고 해결할 수 있습니다. 🚀