WAS에 올라와 있는 한 애플리케이션에서 *Heap Dump 파일(.hprof 파일)이 떨어져, 이번 포스팅을 계기로 Java의 Heap 메모리 영역에 대해 정리하고, OOM을 어떻게 해결할 수 있을 지 알아보려고 한다!

 

*Heap Dump 파일 : Runtime 중인 애플리케이션의 힙 메모리 영역에 대한 스냅샷을 찍어 해당 내역을 저장한 파일을 일컫는다.


Java에서의 메모리 영역

Java로 짜여진 프로그램이 실행되려면, JVM(Java Virtual Machine)이 OS로부터 정해진 메모리를 할당 받고, 해당 JVM 위에서 Java로 짜여진 프로그램이 실행되는 것이다.

이때, JVM에 할당된 메모리 영역(Runtime Data Area)은 용도에 따라 여러 영역으로 나누어 관리되는데, Data type별로 Stack 영역, Heap 영역, Static 영역 등으로 나뉘게 된다. 

한 JVM 에서의 Runtime Data Area 도식화 / 출처 : https://velog.io/@indongcha/Runtime-Data-Area%EC%9D%98-%EA%B5%AC%EC%84%B1

 

출처 : https://www.devkuma.com/docs/jvm/memory-structure/

 

컴퓨터의 메모리는 사용할 수 있는 공간이 무한하지 않으므로, 어떻게 관리하느냐에 따라 프로그램의 성능이 좌우되며, 

이 Java 메모리 영역이 제대로 관리되지 않으면 심할 경우 서비스가 down되기도 한다.

따라서, Java 애플리케이션에서 잘 만들기 위해서는 메모리 구조와 특징에 대해 이해하고 코딩할 필요가 있다.


Java 변수의 종류

Java에서는 Data type 별로 메모리 영역이 나뉜다고 하였으므로, Java에서의 Data type이 어떻게 정의되는 지 볼 필요가 있다.

Java에서의 변수는 크게 4가지로 다음과 같이 나뉜다.

 

  • 클래스 변수(Class Variable, Static Variable)
    • 클래스 영역에서 타입 앞에 static 키워드가 붙는 변수
    • 객체를 공유하는 변수로, 여러 객체에서 공통으로 사용하고 싶을 때 정의
  • 인스턴스 변수(Instance Variable)
    • 클래스 영역에서 static이 아닌 변수
    • 개별적인 저장 공간으로, 객체/인스턴스마다 다른 값을 저장할 수 있음
    • 인스턴스 생성만 하고 참조 변수가 없는 경우에는 GC(Garbage Collector)에 의해 자동 제거된다.
  • 지역 변수 (Local Variable)
    • 메서드 내에서 선언되고, 메서드 수행이 끝나면 소멸되는 변수
    • 초기값을 지정한 후에만 사용 가능
  • 매개 변수(parameter)
    • 메서드 호출 시, '전달하는 값'을 가지고 있는 인수
    • 지역 변수와 동일하게, 선언된 곳부터 수행이 끝날때까지만 유효하다.

1. Method 영역

: JVM이 동작하여 클래스 로더가 클래스를 로딩할 때 곧바로 생성되는 영역으로, JVM이 읽어들인 클래스와 인터페이스에 대한 Runtime Constant Pool, 멤버 변수(필드), 클래스 변수(Static 변수), 상수(final), 생성자(constructor), 메소드(method) 등을 저장하는 공간이다.

  • Method(Static) 영역에 있는 것은 어느 곳에서나 접근이 가능
  • Method(Static) 영역의 데이터는 프로그램의 시작부터 종료까지 컴퓨터의 메모리에 상주한다.
    -> Method 데이터를 무분별하게 많이 사용할 경우, 메모리 부족 현상이 일어날 수 있게 된다.

출처 : https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B7%B8%EB%A6%BC%EC%9C%BC%EB%A1%9C-%EB%B3%B4%EB%8A%94-%EC%9E%90%EB%B0%94-%EC%BD%94%EB%93%9C%EC%9D%98-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD%EC%8A%A4%ED%83%9D-%ED%9E%99#recentEntries

 


2. Stack 영역

: 메소드 내에서 정의하는 기본 자료형에 해당되는 지역변수의 데이터 값이 저장되는 공간이다.

  • 메소드가 호출될 때, 스택 영역에 스택 프레임이 생기고, 스택 프레임 안에 메소드를 호출하게 됨
  • primitive 타입의 데이터(int, double, byte, long, boolean 등)에 해당되는 지역변수, parameter 데이터 값이 저장됨
  • 메소드가 호출될 때 메모리에 할당되고, 종료되면 메모리에서 사라짐
  • Stack은 LIFO(Last-In-First-Out)의 특성을 가지므로, 스코프의 범위를 벗어나면 스택 메모리에서 사라지게 된다.

출처 : https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B7%B8%EB%A6%BC%EC%9C%BC%EB%A1%9C-%EB%B3%B4%EB%8A%94-%EC%9E%90%EB%B0%94-%EC%BD%94%EB%93%9C%EC%9D%98-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD%EC%8A%A4%ED%83%9D-%ED%9E%99#recentEntries

 


3. Heap 영역

: JVM이 관리하는 프로그램 상에서 데이터를 저장하기 위해, Runtime 시 동적으로 할당하여 사용하는 영역이다.

  • 참조형(Reference Type) 데이터 타입을 갖는 객체(인스턴스), 배열 등이 저장되는 공간
  • Heap 영역에 있는 인스턴스들을 가리키는 레퍼런스 변수는 Stack 영역에 적재된다.
  • Heap 영역은 Stack 영역과 다르게, 해당 인스턴스 또는 배열의 호출이 끝나더라도 메모리 할당이 곧바로 해제되지 않는다. 그러다 어떤 참조 변수도 Heap 영역에 있는 인스턴스를 참조하게 않게 되었을 때, GC에 의해 메모리에서 제거된다.
  • Stack 영역은 스레드 갯수마다 각각 생성되는 반면, Heap 영역은 몇개의 스레드가 존재하든 상관없이 JVM 당 하나의 Heap 영역만 존재하게 된다.

OOME(Out Of Memory Error)란?

: JVM의 메모리가 부족하여 발생한 에러로, GC의 한계점에 이르면 OOME가 나타나고, 이때 Heap Dump가 떨어지게 된다.

  • 객체를 생성하는 과정에서 Heap영역에 객체를 할당하기 위한 공간이 부족한 경우 발생
  • GC를 수행하는 데 과도한 시간이 소비되어 메모리를 사용하지 못하는 경우 발생
  • 기본 할당 조건을 충족하지 못하는 경우, 네이티브 라이브러리 코드에 의해서도 발생 가능 (Swap 공간 부족)

OOME의 종류

OutOfMemoryError : Java Heap space

◎ 원인 : 자바 Heap 영역의 공간이 부족하여, 새로운 객체를 생성할 수 없는 경우에 발생한다.

Heap 영역에 대한 공간 확보는 GC를 통해 이루어지는 데, GC를 수행하기 이전에 Heap 메모리 영역이 부족할 경우 발생

  • 가장 많이 확인되는 케이스
  • 메모리 누수가 아닌, 지정된 Heap size가 애플리케이션에 충분히 할당되지 않은 경우에도 발생
  • LifeCycle이 긴 애플리케이션의 경우, finalize를 과도하게 사용할 때 발생하기도 함
    → finalize는 소멸을 명시적으로 할 때 사용하나, GC에 마킹하는 데 오래 걸리는 단점이 있음

 

OutOfMemoryError : GC Overhead limit exceeded

◎ 원인 : Heap 공간이 부족하여 GC가 이루어졌지만, 새로 확보된 Heap 공간이 전체 메모리의 2% 미만일 때 발생

  • ‘Java Heap Space’ 케이스와 유사하게 자주 발생하는 이슈로, 무분별하게 객체를 생성할 경우 발생
  • 더 이상 GC를 할 수 없을 정도로 메모리를 과도하게 사용한다는 것
  • 문제점을 해결하고, 경우에 따라 Heap 메모리 사이즈를 늘리는 것을 추천
  • -XX:-UseGCOverheadLimit 옵션을 사용하여, GC Overhead에 대한 limit 자체를 꺼버릴 수도 있지만, 추천하지 않음

 

OutOfMemoryError : Requested array size exceeds VM limit

◎ 원인 애플리케이션 혹은 애플리케이션을 사용하는 API가 할당된 Heap 공간보다 큰 사이즈의 배열 할당을 시도하는 경우 발생

  • 배열 사이즈를 조정하거나 Heap 메모리 사이즈를 증가시켜서 해결

 

OutOfMemoryError : Metaspace

◎ 원인 : 자바 클래스의 메타데이터(클래스 이름, 생성 정보, 필드 정보, 메서드 정보 등)가 저장되는 *PermGen 또는 *Metaspace의 공간이 부족할 경우 발생. Metaspace의 경우, 자동으로 크기를 증가하는 과정보다 더 많은 메타 데이터들이 저장되면 이 에러가 발생할 수 있다.

  • 클래스의 메타데이터가 저장되는 공간으로, JDK7 이전에는 PermGen으로 정의되었음
  • -XX:MetaspaceSize, -XX:MaxMetaspaceSize 설정을 추가하여 오류를 해결할 수도 있음 (default는 20MB)

 

*PermGen(Permanent Generation) : Java7 에서의 메타데이터가 저장되는 영역

**Metaspace : PermGen 영역은 Heap 메모리에 포함되어, 작은 사이즈로 설정되었기 때문에, GC가 빈번히 발생하였는데, 이러한 문제를 개선하고자 Java8에서는 Heap 메모리 영역에서 분리하여, OS에서 제공하는 native 메모리 영역을 사용하도록 한 영역. GC를 수행하지 않고도 자동으로 크기를 증가 시켜 공간을 확보할 수 있게 되었다.

 

OutOfMemoryError : request size bytes for reason. Out of swap space?

◎ 원인 : 자바 HotSpot VM 코드가 Native heap을 고갈시켜, Native heap에 할당할 수 없는 경우 발생

  • 이 에러 코드와 함께 실패한 요청의 바이트 크기와 메모리 요청의 이유를 나타내며, 대개의 경우 메모리 할당에 실패한 소스 모듈의 이름을 출력한다.
  • Heap 메모리 로그 또는 Memory-map 정보를 분석하는 것이 유용하다.
  • OS의 문제 유틸리티(Native Operation System tools 등)를 사용하여 문제를 진단할 수 있다.

 

OutOfMemoryError : unable to create native thread

◎ 원인 : 시스템 리소스 제한에 도달하여 새로운 네이티브 스레드를 생성할 수 없을 때 발생

  • Thread를 과도하게 많이 사용하는 경우 발생할 수 있음
  • OS의 메모리가 부족해서일 수도 있고, OS 자체적으로 쓰레드 개수를 제한해서일 수도 있음

 


참고

https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B7%B8%EB%A6%BC%EC%9C%BC%EB%A1%9C-%EB%B3%B4%EB%8A%94-%EC%9E%90%EB%B0%94-%EC%BD%94%EB%93%9C%EC%9D%98-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD%EC%8A%A4%ED%83%9D-%ED%9E%99#recentEntries

 

☕ 그림으로 보는 자바 코드의 메모리 영역(스택 & 힙)

자바의 메모리 영역 이번 포스팅에선 자바(JAVA)를 사용하는 입장에서 알아야 할 메모리 구조 및 특징에 대해서 알아보려고 한다. 자바 프로그램이 실행되면 JVM(자바 가상 머신)은 OS로부터 메모

inpa.tistory.com

https://incheol-jung.gitbook.io/docs/q-and-a/java/heap-dump-feat.-oom

 

heap dump 분석하기 (feat. OOM) | Incheol's TECH BLOG

💡 만약 메모리가 8 GB의 인스턴스에서 아무런 설정 없이 애플리케이션을 운영한다면? 최대 메모리 사이즈 : 8GB / 4 = 2GB 초기 메모리 사이즈 : 8GB / 64 = 127MB

incheol-jung.gitbook.io

https://devhtak.github.io/java%20study/2021/07/29/Java_OOM.html

 

Java - OOM 종류 및 조치

Heap

devhtak.github.io

 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기