안녕하세요. 이번 시간에는 지난 시간에 이어 "GC Log 란 ? #2" 편으로 IBM 계열의 Java 메모리 구조, GC 동작 방식, GC 알고리즘 종류에 대해 설명드리도록 하겠습니다.
IBM 계열 Java의 Heap 메모리 구조는 기본적으로 One Heap 구조이며, 1.5 버전부터는 Sun 계열과 같이 Generational Heap 구조를 사용할 수도 있읍니다.
One Heap 구조는 Heapbase 라고 하는 Heap메모리의 처음 주소에서 Heaptop 주소까지 확장이 가능하며 가변크기일 경우 현재까지 할당된 부분을 Heaplimit이라고 합니다.
Heapbase ~ Heaptop 까지는 -Xmx 옵션으로 설정되는 값이며, Heapbase ~ Heaplimit 까지는 -Xmx 옵션으로 설정되는 값입니다.
One Heap 구조는 Java 1.4와 1.5에서 그 구조가 조금 바뀌었는데요.
다음은 Java1.4 버전의 One Heap 구조에 대한 그림입니다.
그리고 다음은 Java1.5 버전의 One Heap 구조에 대한 그림입니다.
각 영역에 대한 설명은 다음과 같습니다.
- K Cluster, P Cluster
Pinned Class Object(K Cluster), Pinned Object(P Cluster)가 할당되는 곳으로 Default 1280개의 class entries가 저장 가능하며, Sun 계열 JVM의 Permanent 영역과 비슷한 역할을 합니다.
K Cluster가 꽉차면 P Cluster에 할당되며 P Cluster가 꽉차게 되면 2kbyte의 새로운 P Cluster가 할당 되는데, 이때 P Cluster 영역이 Heap의 영역에 임의적으로 생성되며 이로 인해 Heap Fragmentation이 발생하기도 합니다.
- Heap
생성된 object들이 저장되는 영역입니다.
- Cache
Cache Allocation을 위한 영역으로 Thread간의 경합을 피하기 위한 THL(Thread Local Heap)과 512byte 이하의 작은 object를 할당하는데 사용됩니다. 여기서 Thread간의 경합이란 실제 이러한 Cache 영역이 없다면 Heap 영역에 대해 object를 할당하기 위해 Heap Lock을 발생시키게 됩니다. 이렇게 되면 Thread들간에 object를 할당하기 이해 Heap에 대한 Lock 경합이 발생하게 됩니다. 이러한 경합을 줄이기 위해 Cache를 이용하여 THL을 두어 해당 Thread가 할당할 수 있는 영역을 별도로 지정하는 것입니다.
- Wilderness OR LOA
Large Object의 할당을 효과적으로 하기 위한 공간으로 주로 64kbyte의 object의 할당에 사용됩니다. 항상 64kbyte 이상의 object가 할당되는 것이 아니라, Heap Fragmentation등의 이유로 Heap영역에 object 할당이 불가능할 경우 64kbyte보다 작은 일반 object들도 할당되기도 합니다.
위에서 살펴본 두버전간 Heap 구조의 차이점은 K Cluster, P Cluster 영역(합쳐서 pinned cluster 영역이라함)이 있느냐 없느냐의 차이입니다.
이렇게 바뀌게된 이유는 pinned object로 인한 Heap Fragmentation 문제에 있습니다.
자, 그럼 여기서 pinned object가 무엇인지 알아 보겠습니다.
pinned object란 고정된 object라는 것으로 원칙적으로 Java에서 모든 object들은 Garbage Collector를 통해 object의 이동이 가능하도록 되어 있습니다. 그러나 일부 특수한 경우는 Garbage Collector가 이동시키지 못하는 object들이 발생합니다.
예들들어 JNI(Java Native Interface)에서 사용하는 object들은 JNI에서 직접 unpinned 될때까지 고정이 됩니다. 또한 class에 대한 메타 정보들도 고정영역으로 지정됩니다.
이러다 보니 실제 pinned object들은 GC에 의해 compaction 작업이 불가능합니다.
이러한 이유로 pinned cluster라는 영역을 두어 해당 영역에 pinned object들을 저장하게 됩니다.
문제는 이러한 pinned cluster 영역 보다 많은 pinned object가 발생하게 되면 pinned object를 저장하기 위해 JVM은 Heap 영역에 pinned object를 위한 공간을 임의로 지정합니다. 이러한 공간이 임시성일 수도 있고 영구적인 공간이 될 수도 있습니다.
이런 현상(pinned cluster영역이 부족할 경우)이 발생하게 되면 Heap 영역에 한번에 할당할 수 있는 일련된 순서의 메모리가 점점 줄어들게 되어 나중에는 별로 크지 않는 object를 할당할려고 해도 allocation failure(AF)이 발생하게 됩니다.
이러한 현상을 Heap Fragmentation이라고 합니다.
1.5 버전에서는 이러한 문제점을 해결하기 위해 pinned object의 저장을 Java Heap이 아닌 System Heap에 저장하도록 변경하였습니다.
1.4 버전에서 발생하는 Heap Fragmentation 문제는 K Cluster와 P Cluster의 크기를 조정을 통해(-Xk, -Xp 옵션 사용) 해결할 수 있습니다.
물론 적정 크기 설정을 위해서는 pinned object의 사용을 모니터링(-Dibm.dg.trc.print=st)을 해야 합니다.
적정 값을 찾기 위한 내용은 포스트를 참고하십시요.
( http://blog.naver.com/bumsukoh/110113098160 )
다음은 Java 1.5 버전 이상에서 사용할 수 있는 Generational Heap 구조에 대해 알아 보겠습니다.
이 Generational Heap 구조는 Sun 계열 Java의 Generation Heap 구조와 매우 흡사합니다.
단, 이 Generational Heap 구조가 Sun 계열 Java에서는 기본적인 구조이지만 IBM 계열 Java에서는 옵션을 통해 지정할 수 있으며 GC에 대한 Policy 옵션중의 하나입니다.
Generational Heap 구조를 사용하기 위해서는 -Xgcpolicy:gencon 옵션을 사용합니다.
다음은 Generational Heap 구조에 대한 그림입니다.
각 영역에 대한 설명은 다음과 같습니다.
- Allocation Space
새롭게 생성되는 object가 할당되는 영역이며, Sun 계열 JVM의 Eden 영역이라 할 수 있습니다.
- Survivor Space
Alive object가 저장되는 공간이며, 실제로 Scavenge로 인한 GC 수행 시 live object들이 copy되는 공간입니다. Sun 계열 JVM의 Survivor 영역이라 할 수 있습니다.
- Tenured Space
Nursery 영역에 저장되어 있었던 object 중에 오래된 object가 이동되어 저장되는 영역입니다.
지금까지 IBM 계열 Java의 Heap 구조에 대해 알아 보았습니다.
다음은 IBM 계열의 Java의 GC 동작 방식에 대해 알아보도록 하겠습니다.
One Heap 구조와 Generational Heap 구조에 따라 GC를 처리하는 방식이 다르게 됩니다.
우선 One Heap 구조의 GC에 대한 부분을 알아 보겠습니다.
One Heap 구조의 GC는 Mark -> Sweep -> Compact 단계를 거쳐 GC가 이루어지게 됩니다.
각 단계는 다음과 같은 역할과 특징을 가집니다.
- Mark 단계
모든 live object들에 대한 mark 작업을 수행합니다. 즉 참조계수(Reference count)가 1이상인 object는 mark 되며 나머지는 garbage로 간주됩니다.
mark된 live object는 markbit vector에 주소가 저장됩니다.
Parallel Mark 기능을 사용할 수 있으며, 다수개의 Thread가 Mark 단계를 수행하기 위해 기동됩니다. 이렇게 Parallel Mark를 위해서는 -Xgcthreads 옵션으로 설정이 가능합니다.
또한 GC로 인한 STW(Stop The World) 시간을 감소시키기 위해 Concurrent Mark 기능을 사용할 수 있습니다.
-Xgcpolicy:optavgpause 옵션 설정을 통해 사용 가능합니다.
Mark 단계를 위한 GC Policy 설정을 위한 옵션의 종류는 4가지가 있으며 다음과 같습니다.
- optthruput
Concurrent mark을 disable 시킵니다. GC로 인해 멈추는 시간에 문제가 없다면(즉, 어플리케이션 수행 응답시간에 영향이 없을때), 이 옵션은 최상의 Throughput을 낼 수 있습니다. 해당 옵션을 gcpolicy의 기본 값입니다.
- optavgpause
Concurrent mark를 enable 시킵니다. GC로 인해 어플리케이션의 응답시간에 문제가 있다면, 응답시간을 줄이기 위해 사용할 수 있습니다. 그 대신 전체적인 Throughput이 감소할 수 있습니다.
- gencon
GC로 인한 멈춤 시간을 최소화하는 것을 위해 Concurrent mark와 Generational Heap을 사용할 수 있습니다.
- subpool
Concurrent mark를 disable 시킵니다. Heap에 object를 할당할 때 더 성능을 향상하키기 위해 개선된 object 할당 알고리즘을 사용하는 것입니다. 이 옵션은 16Core 이상의 SMP 시스템상에서 사용해야 성능을 향상시킬 수 있습니다.
옵션을 선택하는 기준은 해당 시스템의 업무 특성에 따라 다를텐데요.
예를 들어 GC로 인해 멈추는 현상이 특정 시간이 넘으면 절대 안되는 그런 시스템의 경우(공장MES 같이 일정한 응답이 이루어져야 하는 경우)는 optavgpause를 사용하여 전체적인 Throughput이 떨어지더라도 GC로 인한 응답시간은 보장받을 수 있습니다.
- Sweep 단계
Mark 단계에서 live object의 주소 저장소인 markbit vector와 모든 allocation object의 주소 저장소인 allocbit vector와 교차 비교하여 live object외의 garbage object를 삭제하는 작업을 수행합니다.
Parallel Bitwise Sweep 기능을 사용할 수 있으며, Parallel Mark와 같이 다수개의 Thread가 Sweep단계를 수행하기 위해 기동할 수 있습니다. 이러한 Parallel sweep을 위한 thread수는 -Xgcthreadn 옵션을 설정이 가능합니다.
또한 Makr 단계와 같이 Concurrent sweep을 사용할 수 있으며, GC Policy의 optavgpause 옵션을 설정하여 사용가능합니다.
- Compaction 단계
Sweep 단계에서 garbage object들이 삭제되고 나면, compaction 단계에서는 heap 상의 삭제된 garbage object들의 공간에 대해 재정렬 작업을 수행합니다.
이러한 compaction 작업을 수행함으로써 연속된 free memory 공간을 확보할 수 있습니다.
compaction 작업은 object들이 heap memory상에서 이동이 발생하며 이 경우 모든 reference의 변경이 발생할 수 있어 수행 시간이 오래 걸립니다.
compaction 작업은 -Xnocompactgc 이 설정되어 있지 않고 다음과 같은 상황일 경우 발생할 수 있습니다.
- "-Xcompactgc"가 설정된 경우
- Sweep 단계 이후 object 할당을 위한 가용한 free space가 충분하지 않을 경우
- System.gc()가 -Xcompactexplicitgc가 설정되어 있을때 발생할 경우
- 적어도 이전에 가용한 메모리의 절반이 TLH allocation에 사용되었고, 평균 THL 크기가 1024byte 아래로 떨어질 경우
- 가용한 heap이 5%이하일 경우
- 가용한 heap이 128kb 이하일 경우
지금까지 One Heap 구조의 GC 동작 방식에 대해 알아 보았습니다.
One Heap은 간단한 GC 알고리즘(Mark, Sweep, Compact)을 가지고 있으나, 그 내부에 각종 알고리즘과 옵션등으로 인해 복잡하게 느껴집니다.
사실 알고보면 Sun 계열의 Minor GC에 비해 굉장히 간단한 구조입니다(저만의 생각일까요 ㅠ.ㅠ).
다음은 Generational Heap 구조의 GC 동작 방식에 대해 알아 보도록 하겠습니다.
위의 One Heap 구조의 GC에서도 설명드린 -Xgcpolicy:gencon으로 설정 가능한 Generational Heap 구조는 Sun 계열의 GC 방식과 유사하게 동작합니다.
위의 그림은 Generational Heap 구조에 대한 GC 처리 방식입니다.
scavenge 단계에서 Nursery 영역의 Allocate space와 Survivor space를 이용하여 GC를 수행합니다.
그 방식은 allocate space가 가득찰 경우 GC가 발생되며, allocate space의 garbage는 삭제되며, live object는 Survivor space로 복사됩니다.
이러한 단계를 여러번 거치면서 live object는 Tenured space로 옮겨지게 됩니다.
Sun 계열의 Minor GC와 굉장히 유사하다는 것을 알 수 있습니다.
그러나 Sun 계열의 옵션인 -XX:MaxTenuringThreshold 같은 옵션에 대한 내용은 기술 문서에서 찾을 수가 없었습니다(ㅠ.ㅠ).
출처 - http://blog.naver.com/bumsukoh?Redirect=Log&logNo=110119448575&from=postView
'Development > Java' 카테고리의 다른 글
JDBC(Java Database Connectivity) 개요 및 연결 예제 (0) | 2012.02.07 |
---|---|
자바 커뮤니티 프로세스(Java Community Process, 약자 JCP) & JSR (0) | 2012.02.07 |
컨텍스트 리로드(Context reload) (0) | 2012.02.02 |
java - JVM 메모리 및 옵션 (0) | 2012.02.02 |
Heap dump란? (0) | 2012.02.02 |