Computer >> 컴퓨터 >  >> 프로그래밍 >> Java

Java OutOfMemoryError 완벽 가이드: 발생 원리와 OOM 근본 원인 분석 3단계

OutOfMemoryError(메모리 부족 오류)는 JVM(Java Virtual Machine)이 새로운 객체를 할당할 충분한 메모리가 없을 때 발생하는 오류입니다. Java의 예외 클래스 계층 구조에서 OutOfMemoryError는 Exception이 아닌 Error 카테고리에 속하며, 이는 애플리케이션 코드에서 정상적인 복구가 어려운 심각한 문제임을 의미합니다.

OutOfMemoryError 재현하기

OutOfMemoryError를 직접 발생시켜 보면 오류의 동작 방식을 이해하는 데 큰 도움이 됩니다. 재현 과정은 다음과 같습니다.

  • 대용량 메모리를 반복적으로 할당하여 힙(heap) 메모리를 고갈시킵니다.
  • 메모리 할당을 계속 진행하다 보면 JVM이 더 이상 할당할 메모리가 없어지는 시점에 도달하고, 그때 OutOfMemoryError가 발생합니다.
  • 발생한 OutOfMemoryError를 catch 블록으로 잡아 로그를 남기면, 이후 원인 분석에 활용할 수 있습니다.

예제 코드

public class OutOfMemoryErrorDemo {
    public static void main(String[] args) throws Exception {
        int dummyArraySize = 15;
        System.out.println("Max JVM memory: " + Runtime.getRuntime().maxMemory());
        long memoryConsumed = 0;
        try {
            long[] memoryAllocated = null;
            for(int loop = 0; loop < Integer.MAX_VALUE; loop++) {
                memoryAllocated = new long[dummyArraySize];
                memoryAllocated[0] = 0;
                memoryConsumed += dummyArraySize * Long.SIZE;
                System.out.println("Memory Consumed till now: " + memoryConsumed);
                dummyArraySize *= dummyArraySize * 2;
                Thread.sleep(500);
            }
        } catch (OutOfMemoryError outofMemory) {
            System.out.println("Catching out of memory error");
            // 통계 생성을 위해 정보를 기록
            throw outofMemory;
        }
    }
}

위 코드는 배열 크기를 반복마다 급격히 늘려가며 메모리를 소비합니다. 실행 결과는 다음과 같습니다.

실행 결과

Max JVM memory: 119537664
Memory Consumed till now: 960
Memory Consumed till now: 29760
Memory Consumed till now: 25949760
Catching out of memory error
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at OutOfMemoryErrorDemo.main(OutOfMemoryErrorDemo.java:9)

JVM의 최대 메모리(maxMemory())를 초과하는 순간 java.lang.OutOfMemoryError: Java heap space 예외가 발생하는 것을 확인할 수 있습니다.

OOM의 근본 원인을 찾는 단계

실무에서 OutOfMemoryError가 발생하면 단순히 힙 크기를 늘리는 것보다 근본 원인을 파악하는 것이 중요합니다. 다음 세 단계를 따라 하면 효과적으로 원인을 추적할 수 있습니다.

1단계: OutOfMemoryError 발생 시 힙 덤프 생성 설정

애플리케이션 시작 시 VM 옵션 -XX:+HeapDumpOnOutOfMemoryError를 추가합니다. 이 옵션을 지정하면 OOM이 발생하는 순간 JVM이 자동으로 힙 덤프(heap dump) 파일을 생성합니다.

$ java -XX:+HeapDumpOnOutOfMemoryError ...

2단계: 문제 상황 재현

개발 환경에서 문제를 재현하기 어렵다면, 실제 장애가 발생한 운영(production) 환경에서 재현해야 할 수 있습니다. 문제를 재현하여 애플리케이션이 OOM을 던지면, 1단계에서 설정한 옵션에 따라 힙 덤프 파일이 자동으로 생성됩니다.

3단계: 힙 덤프 파일 분석

VisualVM 도구를 사용해 생성된 힙 덤프 파일을 열어 문제를 진단합니다. VisualVM은 JDK 설치 경로의 JDK_HOME/bin/jvisualvm에서 실행할 수 있습니다. 힙 덤프 파일에는 애플리케이션의 메모리 사용 현황에 대한 모든 정보가 담겨 있으므로, 어떤 객체가 과도한 메모리를 점유하고 있는지, 메모리 누수(memory leak)가 있는지 등을 정확하게 파악할 수 있습니다.

이처럼 힙 덤프 확보 → 문제 재현 → 덤프 분석의 흐름으로 접근하면, 추측이 아닌 데이터에 기반해 OOM의 근본 원인을 해결할 수 있습니다.