GC 로그 수집과 분석 JVM에서는 GC 상황에 대한 로그를 남기기 위해 옵션을 제공하고 있다. 자바의 경우 -verbosegc라는 옵션을 주면되고 HP-UX의 경우 -verbosegc -Xverbosegc 옵션을 주면 좀 더 자세한 GC 정보를 얻을 수 있다. GC 정보는 stdout으로 출력되기 때문에 “>” redirection 등을 이용해서 파일에 저장해놓고 분석할 수 있다.
Example ) java -verbosegc MyApplication
![](https://img1.daumcdn.net/relay/cafe/original/?fname=http%3A%2F%2Fwww.imaso.co.kr%2Fimages%2Fimage%2F2004-07-292-01.jpg)
![](https://img1.daumcdn.net/relay/cafe/original/?fname=http%3A%2F%2Fwww.imaso.co.kr%2Fimages%2Fimage%2F2004-07-292-02.jpg)
그럼 실제로 나온 GC 로그를 어떻게 보는지 알아보자. <그림 10>은 GC 로그 결과를 모아 놓은 내용이다(실제로는 애플리케이션의 stdout으로 출력되는 내용과 섞여서 출력된다). Minor GC는 [GC로 표기되고, Full GC는 [Full GC로 표기된다. 그 다음 값은 Heap size before GC인데, GC 전에 Heap 사용량(New/Young 영역+Old 영역+Perm 영역)의 크기를 나타낸다. Heap size after GC는 GC가 발생한 후에 힙의 사용량이다. Minor GC가 발생했을 때는 Eden과 Survivor 영역으 GC가 되므로 Heap size after GC는 Old 영역의 용량과 유사하다(Minor GC에서 GC되지 않은 하나의 Survivor영역 내의 오브젝트들의 크기도 포함해야 한다). Total Heap Size는 현재 JVM이 사용하는 힙 메모리 양이다. 이 크기는 자바에서 -ms와 -mx 옵션으로 조정이 가능한데. 예를 들어 -ms512m -mx1024m로 해놓으면 자바 힙은 메모리 사용량에 따라 512~1024MB 사이의 크기에서 적절하게 늘었다 줄었다 한다(이 늘어나고 줄어드는 기준은 -XX:MaxHeapFreeRatio와 -XX:MinHeapFreeRation를 이용해서 조정할 수 있으나 JVM 벤더에 따라 차이가 나기 때문에 각 벤더별 JVM 매뉴얼을 참고하기 바란다). 파라미터에 대한 이야기는 추후에 좀 더 자세히 하도록 하자. 그 다음 값은 GC에 소요된 시간이다. <그림 10>의 GC 로그를 보면 Minor GC가 일어날 때마다 약 20MB 정도의 컬렉션이 일어난다. Minor GC는 Eden과 Suvivor 영역 하나를 GC하는 것이기 때문에 New/Young 영역을 20MB 정도로 생각할 수 있다. Full GC 때를 보면 약 44MB에서 1749KB로 GC가 되었음을 볼 수 있다. Old 영역에 큰 데이터가 많지 않은 경우이다. 데이터를 많이 사용하는 애플리케이션의 경우 전체 힙이 512MB이라고 가정할 때, Full GC 후에도 480MB 정도로 유지되는 경우가 있다. 이런 경우에는 실제로 애플리케이션에서 메모리를 많이 사용하고 있다고 판단할 수 있기 때문에 전체 힙 크기를 늘려줄 필요가 있다. 이렇게 수집된 GC 로그는 다소 보기가 어렵기 때문에 좀 더 쉽게 분석할 수 있게 하기 위해 GC 로그를 awk 스크립트를 이용해서 정제하면 분석이 용이하다.
BEGIN{ printf("Minor\tMajor\tAlive\tFree\n"); } { if( substr($0,1,4) == "[GC "){ split($0,array," "); printf("%s\t0.0\t",array[3])
split(array[2],barray,"K") before=barray[1] after=substr(barray[2],3) reclaim=before-after printf("%s\t%s\n",after,reclaim) }
if( substr($0,1,9) == "[Full GC "){ split($0,array," "); printf("0.0\t%s\t",array[4])
split(array[3],barray,"K") before = barray[1] after = substr(barray[2],3) reclaim = before - after printf("%s\t%s\n",after,reclaim) } next; }
이 스크립트를 작성한 후에 유닉스의 awk 명령을 이용해서 다음과 같이 쳐주면 정리된 형태로 GC 로그만 추출하여 보여준다.
% awk -f gc.awk GC 로그파일명
Minor Major Alive Freed 0.0484179 0.0 20909 19640 0.0411095 0.0 21405 19792 0.0846190 0.0 22995 18698 0.0492838 0.0 23672 19611 0.0 0.1452965 1749 42211 0.0310949 0.0 2810 19227 0.0469624 0.0 3657 19441 0.0580108 0.0 4847 19098
Minor와 Major는 각각 Minor GC와 Full GC가 일어날 때 소요된 시간을 나타내며, 얼라이브는 GC 후에 남아있는 메모리 양 그리고 Freed는 GC에 의해 컬렉트된 메모리 양이다. 이 로그 파일은 excel 등을 이용하여 그래프 등으로 변환해서 보면 좀 더 다각적인 분석이 가능해진다. JDK 1.4에서부터는 -XX:+PrintGCDetails 옵션이 추가되어서 더 자세한 GC 정보를 수집할 수 있다.
GC 관련 파라미터 GC 관련 설정 값을 보기 전에 앞서 -X와 -XX 옵션에 대해 먼저 언급하자. 이 옵션들은 표준 옵션이 아니라 벤더 별 JVM에서 따로 제공하는 옵션이기에 예고 없이 변경되거나 없어질 수 있다. 따라서 사용 전에 미리 JVM 벤더 홈페이지를 통해 검증한 다음 사용해야 한다.
전체 힙 크기 조정 옵션 전체 힙 크기는 -ms와 -mx로 힙 크기의 영역을 조정할 수 있다. 예를 들어 -ms512m -mx 1024m로 설정하면 JVM은 전체 힙 크기를 애플리케이션의 상황에 따라 512~1024MB 사이에서 사용하게 된다. 메모리가 모자를 때는 힙을 늘리고, 남을 때는 힙을 줄이는 heap growing과 shirinking 작업을 수행하는데, 메모리 변화량이 큰 애플리케이션이 아니라면 이 min heap size와 max heap size는 동일하게 설정하는 것이 좋다. 일반적으로 1GB까지의 힙을 설정하는 데에는 문제가 없으나, 1GB가 넘는 대용량 메모리를 설정하고자 할 경우에는 별도의 JVM 옵션이 필요한 경우가 있기 때문에 미리 자료를 참고할 필요가 있다.
※ IBM AIX JVM의 경우 %export LDR_CNTRL=MAXDATA=0x10000000 %java -Xms1500m -Xmx1500m MyApplication
Perm 크기 조정 옵션 Perm 크기는 앞에서도 설명했듯이 자바 애플리케이션 자체(자바 클래스 등)가 로딩되는 영역이다. J2EE 애플리케이션의 경우에는 애플리케이션 자체의 크기가 큰 편에 속하기 때문에 기본 값으로 설정된 Perm 크기로는 애플리케이션 클래스가 로딩되기에 모자란 경우가 대부분이기 때문에, WAS 시작 초기나 가동 초기에 ‘Out Of Memory’ 에러를 유발하는 경우가 많다. PermSize는 -XX:MaxPermSize =128m 식으로 지정할 수 있다. 일반적으로 WAS에서 PermSize는 64~256MB 사이가 적절하다.
New 영역과 Old 영역의 조정 New 영역은 -XX:NewRatio=2에 의해 조정된다. NewRatio Old/New 크기의 값이다. 전체 힙 크기가 768일 때, NewRatio=2이면 New 영역이 256MB, Old 영역이 512MB로 설정된다. JVM 1.4.x에서는 -XX:NewSize=128m 옵션을 이용해서 직접 New 영역의 크기를 지정하는 것이 가능하다.
Survivor 영역 조정 옵션 -XX:SurvivorRatio=64(eden/survivor의 비율):64이면 eden이 128MB일 때 survivor 영역은 2MB가 된다.
-server와 -client 옵션 JVM에는 일반적으로 server와 client의 두 가지 옵션을 제공한다. 결론만 말하면 server 옵션은 WAS와 같은 서버 환경에 최적화된 옵션이고, client 옵션은 워드프로세서와 같은 클라이언트 애플리케이션에 최적화된 옵션이다. 그냥 언뜻 보기에는 단순한 옵션 하나로 보일 수 있지만 내부에서 돌아가는 핫스팟 컴파일러(hotspot compiler)에 대한 최적화 방법과 메모리 구조 자체가 아예 다르다.
-server 옵션 서버용 애플리케이션에 최적화된 옵션이다. 서버 애플리케이션은 부트업(boot up) 시간보다는 사용자에 대한 응답 시간(response time)이 중요하고, 많은 사용자가 동시에 사용하기 때문에 세션 등의 유저 데이터(user data)를 다루는 게 일반적이다. 그래서 server 옵션으로 제공되는 핫스팟 컴파일러는 자바 애플리케이션을 최적화해서 빠른 응답 시간을 내는 데 집중되어 있다. 또한 메모리 모델 역시 서버의 경우에는 특정 사용자가 서버 운영시간 동안 계속 서버를 사용하는 게 아니기 때문에(로그인하고 사용한 후에는 로그아웃되기 때문에) 사용자에 관련된 객체들이 오래 지속되는 경우가 드물다. 그래서 상대적으로 Old 영역이 작고 New 영역이 크게 배정된다(<그림 11> 참조).
![](https://img1.daumcdn.net/relay/cafe/original/?fname=http%3A%2F%2Fwww.imaso.co.kr%2Fimages%2Fimage%2F2004-07-294.jpg)
-client 옵션 클라이언트 애플리케이션은 워드프로세서처럼 혼자 사용하는 애플리케이션이다. 그래서 클라이언트 애플리케이션은 응답 시간보다는 빨리 기동되는 데에 최적화되어 있다. 또한 대부분의 클라이언트 애플리케이션을 구성하는 오브젝트는 GUI 컴포넌트와 같이 애플리케이션이 종료될 때까지 남아 있는 오브젝트의 비중이 높기 때문에 상대적으로 Old 영역의 비율이 높다. 이 두 옵션은 가장 간단한 옵션이지만 JVM의 최적화에 아주 큰 부분을 차지하고 있는 옵션이기 때문에 반드시 애플리케이션의 성격에 맞춰서 적용하기 바란다(참고로 썬 JVM은 기본 값이 client, HPJVM는 기본 값이 server로 셋팅되어 있다)
GC 방식에 대한 옵션 GC 방식에 대한 옵션은 앞에서도 설명했지만, 일반적인 GC 방식 이외에 Concurrent GC, Parallel GC, Inceremental GC와 같이 추가적인 GC 알고리즘이 존재한다.
| |