오늘은 어디로 갈까...

자바 메모리에 관해서 본문

낙서

자바 메모리에 관해서

剛宇 2009. 3. 16. 17:51
 프로그램은 실행되면서 저장할 데이터가 있으면 메모리의 일정 공간을 할당받아서 사용하게 된다. 그런데 이 데이터가 더 이상 사용할 필요가 없는것이면, 사용항 메모리를 반납해야주어야한다.
 자바는 친절히도 메모리 회수를 자동적으로 해준다. 즉, JVM(Java Virtual Machine)의 Garbage Collector란 놈이 사용하지 않는 메모리를 알아서 회수해주는것이다. 이 회수에 대한 행위를 GC(Garbage Collection)이라한다.

JVM의 메모리 영역은 크게 Heap Area과 Non-Heap Area으로 나눌수 있다.

1. Heap Area
 힙 영역(Heap Area)은 동적으로 할당하여 사용할 수 있는 메모리 영역으로서, 주로 실행중에 생성되는 객체들이 저장된다. 이 힙 영역(Heap Area)이 GC(Garbage Collection)의 대상이 되는 메모리 영역이다. 즉, Garbage Collector에 의해서 이 힙영역중에 사용하지 않는 메모리가 회수되는것이다.
 힙 영역(Heap Area)을 세분화하면 일반적으로 세 영역으로 나눌 수 있다.
1.1. Young(New) Object Space : 새로 생성한 객체를 저장하능 영역이다. 이 부분은 다시 세 부분으로 나누어진다.
 새로 생성한 모든 객체들이 가는 Eden 영역과 Old Object Space가기전에 거치는  SS1(Survivor Space 1), SS2(Survivor Space 2) 영역(From Space, To Space 라고 부르기도 한다.)이 있다. 이 Young 영역의 GC를 Minor GC(Scavenge GC)라 부른다. 객체가 탄생(?)하면 Eden 영역에 놀다가, Minor GC가 발생하면 살아있는것들은 S1으로 보내고, 나머지는 제거(?)해버린다. 그리고 다음번 Minor GC가 발생하면 S1 영역의 객체중 살아있는것은 SS2로 복사(실제적으로 레퍼런스 주소만 변경)한후 Eden, S1 영역을 초기화(Clear) 해버린다. 이렇게 Minor GC를 실행하다가 오래된 객체를 Old Object Space로 옮기는것이다.
1.2. Old Object Space: Young(New) 영역에서 살아남은(?) 객체가 이동되어 저장되는 영역이다.
 이 Old 영영의 GC를 Full GC라 부른다. 일반적으로 사용되는 Full GC 알고리즘은 사용하는 객체들의 레퍼런스(reference)를 찾아 연결되지 않는 객체를 표시(Mark)한 다음, 작업이 끝난후 표시한 객체를 모두 삭제를 하는 방법이다. Full GC는 속도가 매우 느리고, 실행되는 동안에 순간적으로 프로그램이 멈춰버리기 때문에 어플리케이션(Application)의 성능에 큰 영향을 준다. 그래서 여러가지 GC 알고리즘을 지원한다.
1.3. Permanet Space : 클래스와 메소드등의 메타정보를 저장하는 영역이다. (논리적인 Heap 영역으로서, -Xms, -Xmx에서 말하는 Heap 영역에서는 제외된다.)

2. Non-Heap Area
 Non-Heap 영역은 Heap 이외의 영역을 말한다.(당연한 말을 ^^;)
2.1. Method Area : 메소드와 클래스 변수를 저장하기 위한 영역이다.
2.2. Stack Area : 메소드 호출 시 메소드의 매개변수, 지역변수, 임시변수등을 저장하기 위한 스택 구조의 영역이다.
2.3. 기타 : JVM이 현재 수행할 명령어의 주소를 저장하는 PC 레지스터, native 메소드의 매개변수, 지역변수 등을 저장 native 메소드 스택등이 있다.


3. JVM 실행 Option
참고 : http://blogs.sun.com/watt/resource/jvm-options-list.html
 - 이 옵션들을 JVM에 따라 형식이 조금씩 다를 수 있고, 지원하지 않을 수도 있다.
3.1. -X Option
-Xms : 초기 Heap size (Young + Old)
-Xmx : 최대 Heap size (Young + Old)
-Xss : 각 Thread별 Stack size
-Xmn : Young(New) 영역 크기

3.2. -XX Option
-XX:PermSize : 초기 Permanent size
-XX:MaxPermSize : 최대 Permanent size
-XX:SurvivorRatio=<n> : Eden 영역의 크기를 SS1 또는 SS2의 크기로 나눈값
-XX:NewRatio : Old/New Size 값이다. (전체 Heasp Size가 768m일때 NewRatio=2이면, New=256m, Old=512m으로 설정된다.)


3.3. 메모리 산출 공식
YOUNG  = Xmn = Eden + SS1 + SS2
Eden = YOUNG - ((Xmn/(SurvivorRatio + 2)) * 2)
SS1 = (YOUNG - Eden)/2
SS2 = (YOUNG - Eden)/2
OLD = Xmx - Xmn

3.4. -verbosegc
자바 실행시 JVM 옵션에 -verbosegc 를 주면 GC 로그를 볼 수 있다.
* 실행 결과
[GC 5971K->5481K(7748K), 0.0011015 secs]
[GC 5993K->5498K(7748K), 0.0010708 secs]
[GC 6008K->5523K(7748K), 0.0011719 secs]
[GC 6027K->5588K(7748K), 0.0010359 secs]
[GC 6089K->5695K(7748K), 0.0012088 secs]
[Full GC 5737K->5641K(7748K), 0.0630893 secs]
[GC 6345K->5945K(10172K), 0.0017444 secs]
[GC 6649K->5944K(10172K), 0.0010166 secs]
[GC 6648K->6000K(10172K), 0.0011124 secs]
[GC 6704K->6048K(10172K), 0.0015901 secs]
[GC 6752K->6059K(10172K), 0.0006417 secs]
[GC 6763K->6116K(10172K), 0.0006613 secs]

[GC 6763K->6116K(10172K), 0.0006613 secs]
Minor GC는 "GC"로, Full GC는 "Full GC"로 나타난다.
6763K은 GC하기 전의 Heap Size이고, 6116K은 GC후의 Heap 사이즈를 나타낸다.
(10172K)은 총 Heasp Size이고, 0.0006613 secs은 GC 소요 시간이다.

4. java.lang.OutOfMemoryError
  - 잊혀질만하면 나타나는 우리의 친구 OutOfMemory. 프로그램에 메모리 누수등의 문제가 없다면, JVM 옵션으로 메모리를 늘려주면 된다. 이 OutOfMemory도 메모리 종료(?)별로 발생하는데, 가장 빈번하게 발생하는 놈은 heap과 PermGen이라는 놈이다.
4.1. java.lang.OutOfMemoryError: Java heap space
 - 가장 유명한 놈으로서, Heap 크기가 부족해서 나는것이다.
 - 해결책 : -Xmx 옵션을 이용해서 최대 Heap Size를 늘려준다.
4.2.java.lang.OutOfMemoryError: PermGen space
 - 요즘 뜨는 놈인데, 동적 클래스를 많이 사용할 경우 발생한다. JSP->Server 변환이나, spring 같은 동적 클래스를 많이 사용하는 프레임워크를 사용할때 종종 발생한다.
 - 해결책 : -XX:MaxPermSize 옵션을 이용해서 Perm Size를 늘려준다.
4.3. 기타
 - 이 외에도 Native Heap Space가 부족하거나, Thread Stack Space가 부족할 경우 발생하기도 하는데, 보통은 거의 볼 수 없으므로 생략.(Native Heap Space가 부족할 경우는 Heap 사이즈를 줄여주면 되고, Thread Stack Space가 부족할경우는 Thread 수를 줄이던지 Stack Size를 줄이면 된다.)
 - 32bit JVM일 경우 일반적으로 사용 가능한 메모리 크기는 4G이다.(OS에 따라 다르기도 하다) 232=4*230=4G이니까.
 - 단순히 메모리 부족(?) 때문에 생기는 문제라면, 위에처럼 메모리를 늘려주면 된다. 하지만 프로그램의 버그때문에 메모리 누수현상이 발생하여 에러가 발생할경우는 참 난감할것이다. 이럴 경우는 HProf나 Heap Dump를 이용해서 잘못된 부분을 찾는 수밖에 없다. 요즘은 JMX(Java Management eXtensions)을 이용해 실시간 모니터링도 되니 정말 편한 세상이다. (이부분에 대해서 다음에 심심하면 한번 알아보자 ^^;)



(그림을 그리는게 한결 이해하기 쉬운데, 그림 그리는데 재주가 없는지라 글로만 작성해서 죄송합니다.~~)