프로그래밍/JAVA2009. 6. 25. 23:07


서버 어플리케이션등 장기간 동작해야하는 프로그램이 어느날 갑자기 메모리 할당을 실패하며 Out Of Memory Exception(OOME)를 발생시키는 경우를 종종 볼수 있다.
 
OOME의 발생 원인은 매우 다양한데 이는 JVM이 사용하는 메모리 공간의 다양성에 기인한다. JVM의 메모리 구분은 다음과 같다.

1. JAVA Heap: 사용자 Object들이 거주하는 공간인다. -Xms<size>와 -Xmx<size> Option에 의해 크기가 결정된다.

** JVM의 Heap 메모리 모델에 대해서는 [http://lyb1495.tistory.com/entry/JAVA-메모리-모델] 참조

2. Permanent Space: Class에 대한 메타 정보를 저장하는 공간이다. PermSize와 MaxPermSize 옵션에 의해 크기가 결정된다.

3. Native Heap: Java Object가 아닌 Native Object들이 거주하는 공간이다. Native Heap의 크기는 JVM Option으로 지정할 수 없으며, OS 차원에서 결정된다.

각 메모리 공간의 용도와 사용 방식이 다르기 때문에 OOME 또한 매우 다양한 상황에서 발생하게 된다. OOME가 발생하는 정확한 원인을 분석하려면 각 메모리 공간의 특성을 이해하고 그에 맞는 해결책을 모색해야 한다.

특히 가장 빈번하게 발생되며 JAVA 프로그래머들의 골치를 아프게하는 JAVA Heap에서의 OOME에 대해 알아보자.

** 비록 JAVA 언어와 JVM이 자동화된 메모리 관리 기능을 제공하지만, 이것이 개발자나 관리자가 메모리 관리에 대해 무신경해도 된다는 것을 의미하지 않는다는 사실을 명심하자. JAVA 에서도 잘못된 메모리 관리는 여전히 많은 문제를 일으키며, Garbage Collection에 의한 성능저하나 OOME에 의한 Applictaion 정지나 System Crash등이 대표적인 예이다.

JAVA Heap에서 OOME가 발생하는 경우에는 다음과 같은 에러 메시지가 출력된다.

 Exception in thread "main": java.lang.OutOfMemoryError: Java heap space

또는

 Exception in thread main: java.lang.OutOfMemoryError: Requested array size exceeds VM limit

전자는 JAVA Heap의 부족으로 Object를 생성하지 못하는 경우에 발생한다. 후자의 메시지는 Java Heap의 최대 크기보다 큰 Array가 요청되는 경우에 발생한다. 가령 Java Heap의 최대 크기가 256M인 상황에서 300M 크기의 Array를 생성하는 경우가 이에 해당한다.

JAVA Heap에서 OOME가 발생하는 이유는 다음과 같다.

  • JAVA Heap이 작은 경우
  • Memory Leak이 발생하는 경우
    • Application Logic에 의한 Memory Leak
    • JDK 버그나 WAS 버그에 의한 Memory Leak
  • finalize 메소드에 의한 Collection 지연

 

1. JAVA Heap 크기와 OOME
JAVA Heap의 최대 크기가 Application의 메모리 요구량에 비해 작게 설정된 경우에 OOME가 발생한다. Memory Leak이 발생하지 않는데도 OOME가 발생한다면 Java Heap의 크기가 부족하다고 판단할 수 있다. -Xmx<size> 옵션을 이용해서 Java Heap의 최대 크기를 키워주어야 한다.

2. Memory Leak과 OOME
Memory Leak이 발생하는 경우에는 Java Heap의 크기와 무관하게 OOME가 발생할 수 있다. 아무리 Java Heap의 크기를 크게 하더라도 결국 Memory Leak에 의해 Collection되지 않는 Garbage 객체들이 메모리를 다 소진하기 때문이다. Memory Leak은 대부분 Application Logic 상의 오류에 의해 발생한다. Object에 대한 참조(Reference) 관계가 복잡한 경우 조그마한 실수로 인해 사용되지 않은 Object를 계속해서 참조하게 된다. 이러한 Object들은 비록 Application에서는 사용되지 않지만 Garbage Collection에 의해 메모리 해제가 이루어지지 않기 때문에 OOME를 유발하게 된다.
JDK Bug나 WAS Bug에 의해서도 Memory Leak이 발생할 수 있다. JDK가 제공하는 라이브러리나 WAS가 제공하는 라이브러리에서 로직 오류로 인한 Memory Leak 가능성이 있기 때문이다. Application Logic에서 Memory Leak이 검출되지 않는 경우에는 JDK나 WAS의 Bug를 의심해볼 필요가 있으며 각 Vendor가 제공하는 Bug Database[SUN JVM: http://bugs.sun.com/]를 통해 검색 가능하다.

** Memory Leak에 대해서는 [http://lyb1495.tistory.com/entry/JAVA-Memory-Leak] 참조

3. finalize 메소드에 의한 Collection 지연과 OOME
특정 Class에 finalize 메소드가 정의되어 있는 경우, 이 Class Type의 Object는 Garbage Collection 발생시 즉각적으로 Collection 되지 않는다. 대신 Finalization Queue에 들어간 후 Finalizer에 의해 정리가 된다. Finalizer는 Object의 finalize 메소드를 실행한 후 메모리 정리 작업을 수행한다. 만일 finalize 메소드를 수행하는데 오랜 시간이 걸린다면 그 만큼 객체가 오랫동안 메모리를 점유하게 된다. 이로 인해 OOME가 발생할 확률이 높아진다. 이런 이유 때문에 finalize 메소드는 되도록 사용하지 말아야 한다.

'프로그래밍 > JAVA' 카테고리의 다른 글

Java 쓰레드  (0) 2009.12.04
스트러츠 properties 한글 편집  (0) 2009.12.03
Java XML Parser JDOM  (2) 2009.09.03
JRE Detection  (0) 2009.08.11
자바 웹 스타트(Java Web Start)  (0) 2009.08.07
JFreeChart with SWT  (0) 2009.07.14
자바 데몬(daemon) 만들기  (2) 2009.07.08
LRU 캐쉬 엔진의 구현  (0) 2009.06.26
JAVA Memory Leak  (2) 2009.06.25
Out Of Memory Error(OOME)에 대하여  (0) 2009.06.25
JAVA Heap 메모리 모델  (2) 2009.06.24
Posted by devop
TAG , , ,

댓글을 달아 주세요