Heap

image

  • Java 7 까지의 Heap 구조, 8부터 PermGen 부분이 Metaspace로 변경되었다.
    • PermGen 은 class 혹은 method 코드가 저장되는 영역으로 Heap영역에 위치한다.
    • Metaspace는 Classloader가 로드한 class 들의 metadata를 저장되는 공간으로 Heap이 아닌 Native 영역에 위치시킨다.
    • 가장 큰 차이는 클래스의 metadata를 native 영역으로 옮김으로써 OS가 동적으로 조정할 수 있도록 한 것
  • Runtime Data Area는 JVM이 OS로부터 할당받은 메모리 공간을 말하며 크게 5가지 영역으로 나눌 수 있다.
  • Heap은 런타임시 동적으로 할당하여 사용하는 영역으로 class를 이용해 instance를 생성하면 Heap에 저장된다.

메모리 누수(Memory Leak)

  • 메모리 사용량
    • 시스템 작업관리자에서 나오는 메모리 사용량으로는 측정의 정확도가 매우 떨어진다.
    • Runtime 클래스에 totalMemory() - freeMemory()를 통해 자바 어플리케이션의 메모리 사용량을 로그로 출력
      • long freeMemory() : Returns the amount of free memory in the jvm.
      • long totalMemory() : Returns the total amount of memory in the jvm.
    • Jprobe 또는 Optimizeit와 같은 개발도구로 측정
  • 메모리 누수 발생 요인
    • Java에서는 GC에 의해 메모리가 자동 관리 되지만 memory leak이 발생할 수 있다.
    • 루트 참조들로부터 직간접적으로 참조가 되는(reachable) 모든 객체를 현재 사용중인 객체라고 판단하고, 나머지는 쓰레기 객체라고 판단하여 JVM은 이러한 쓰레기 객체를 수거한다.
      • 루트 참조는 다음과 같이 크게 3가지가 존재한다.
        • static 변수에 의한 객체 참조
        • 현재 자바 스레드 스택내의 지역변수, 매개 변수에 의한 객체 참조
        • JNI 프로그램에 의해 동적으로 만들어지고 제거되는 JNI global 객체 참조
    • 하지만, 실제로 사용되지 않는 객체의 reference를 프로그램에서 잡고 있으면 GC에 의해 처리되지 않고, 프로그램 내에서도 접근하여 사용될 수 없는 쓰레기 객체로 메모리를 점유하게 된다.
      • 상호참조가 많은 경우 하나의 객체 참조를 null처리해주지 않은 실수
    • 이로 인해 메모리 누수가 발생하며 OOM이 발생하여 프로그램이 종료되는 심각한 현상이 발생한다.

OOME(OutOfMemoryError)

  • 객체를 생성하는 과정에서 힙 공간에 객체를 할당하기 위한 공간이 부족할 경우 발생한다.
  • 해당 경우 GC는 새로운 객체를 생성할 수 있는 공간을 확보할 수 없거나 GC를 수행하는 데 과도한 시간이 소비되어 메모리를 사용하지 못하는 상황에서 발생할 수 있다.
  • 기본 할당조건을 충족하지 못하는 경우 네이티브 라이브러리 코드에 의해 발생할 수 있다. (스왑 공간 부족)

OOME 종류 및 원인

  • java.lang.OutOfMemoryError: Java heap space(Java 힙 공간)
    • 원인
      • 자바 힙 공간에 새로운 객체를 생성할 수 없는 경우에 발생한다.
      • 메모리 누수가 아닌 지정한 힙 크기(혹은 기본 크기)가 애플리케이션에 충분하지 않은 경우에도 발생한다.
      • 혹은, 생명주기가 긴 애플리케이션의 경우 finalize를 과도하게 사용할 때 발생하기도 한다.
        • finalize는 소멸을 명시적으로 할 때 사용하나 GC에 마킹하는 데 오래걸리는 단점이 있다.
    • 조치
      • JVM Option 을 통해 Heap size 를 늘려 해결할 수 있다.
  • java.lang.OutOfMemoryError: GC Overhead limit exceeded(GC 오버헤드 한도 초과)
    • 원인
      • 메모리가 부족하여 가비지 컬렉션이 이루어졌지만, 새로 확보된 메모리가 전체 메모리의 2% 미만일 때 발생한다.
      • 더 이상 가비지 컬렉션을 할 수 없을 정도로 메모리를 사용한다는 것이다.
    • 조치
      • 힙 크기를 늘린다.
      • XX:-UseGCOverheadLimit 선택사항을 추가하여 java.lang.OutOfMemoryError가 발생하는 초과 오버헤드 GC 제한 명령을 해제할 수 있다.
  • java.lang.OutOfMemoryError: Requested array size exceeds VM limit(요청 배열 크기가 VM 제한 초과)
    • 원인
      • 애플리케이션(혹은 애플리케이션을 사용하는 API)이 힙 공간보다 큰 배열 할당을 시도하는 경우 발생한다.
      • 예를 들어, 애플리케이션이 512MB 크기의 배열을 할당하려하지만 힙의 최대 크기가 256MB인 경우 요청 배열 크기가 VM 제한을 초과하면서 java.lang.OutOfMemoryError를 던진다.
      • 힙 공간 사이즈가 너무 작은 경우
        • 배열 요소를 계산하고 더하는 등 배열을 키우는 경우
  • java.lang.OutOfMemoryError: Metaspace
    • 원인
      • 자바 클래스 메타데이터는 원시 메모리(=메타 공간)에 할당된다.
      • 클래스 메타데이터가 할당될 메타 공간이 모두 소모되면 java.lang.OutOfMemoryError: Metaspace가 발생한다.
      • 클래스 메타데이터가 할당될 공간은 MaxMetaSpaceSize 매개변수로 제한된다.
    • 조치
      • MaxMetaSpaceSize 값을 늘려 설정한다. MaxMetaSpaceSize는 자바 힙과 동일한 주소 공간에 할당된다.
      • 자바 힙의 크기를 줄이면 더 많은 공간을 확보할 수 있다. 자바 힙 공간에 여유가 있는 경우에 고려해 볼 수 있다.
      • heap영역과 perm영역을 과하게 설정하면, native영역과 stack영역이 적은 공간으로 설정되어 이 두 영역의 공간부족에러가 발생 할 수도 있다.
  • java.lang.OutOfMemoryError: request size bytes for reason. Out of swap space?
    • 원인
      • 자바 HotSpot VM 코드가 네이티브 힙 고갈이 되어 네이티브 힙에 할당할 수 없는 경우 발생한다.
      • 이 메시지는 실패한 요청의 바이트 크기와 메모리 요청의 이유를 나타내며 대개의 경우 할당에 실패한 소스 모듈의 이름을 출력한다.
    • 조치
      • 네이티브 힙 고갈의 경우는 힙 메모리 로그 및 메모리 맵 정보를 분석하는 것이 유용하다.
      • 이런 유형은 운영체제의 문제 유틸리티를 사용하여 문제를 진단할 수 있다.
  • java.lang.OutOfMemoryError: Compressed class space(압축된 클래스 공간)
    • 원인
      • 64비트 플랫폼에서 클래스 메타데이터 포인터는 32비트 오프셋(UseCompressedOops)으로 표현된다.
      • 이 방식은 UseCompressedClassPointers(기본값 활성화, on)으로 제어할 수 잇으며 활성화되면 클래스 메타데이터가 사용할 수 있는 공간의 크기가 고정된다.
      • UseCompressedClassPointers에 필요한 CompressedClassSpaceSize를 초과하면 java.lang.OutOfMemoryError: Compressed class space를 던진다.
    • 조치
      • CompressedClassSpaceSize 크기를 키우거나 UseCompressedClassPointers를 비활성화 시킨다.
  • java.lang.OutOfMemoryError: Reason stack_trace_with_native method
    • 원인
      • 이 메시지가 출력되는 것은 원시 메서드에서부터 스택 트레이스가 출력되었다는 것을 의미하며, 네이티브 메서드에 할당 오류가 발생했음을 의미한다.
      • 이 메시지가 위의 메시지들과 다른 점은 JVM 코드가 아니라 Java Native Interface(JNI) 또는 원시메서드에서 할당실패가 감지되었다는 것이다.
    • 조치
      • 이 예외가 발생하면 운영체제가 제공하는 유틸리티를 이용해서 문제점을 진단해야 한다.

해결 사례 feat 우형 기술블로그

  • https://techblog.woowahan.com/2628/

출처

  • OOME: https://velog.io/@jsj3282/Heap-Memory-Leak-OutOfMemoryError
  • http://ssiso.net/cafe/club/club1/board1/content.php?board_code=javaStudy%7CjavaTuning&idx=7401&club=javaStudy&cp=1&cb=1&search=&search_word=
  • Runtime class: https://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html
  • Memory leak: https://blog.naver.com/deogtae/20014163212