티블로그에서 이전
이클립스 메모리 튜닝에 대한 내용을 담고 있습니다.
Sun JVM 1.6 이하 버전에서만 사용된 지식입니다.
VM 설정
Sun JDK를 설치하면 jdk와 jre 두가지를 모두 설치할 수 있다는 것을 알 것입니다.
두가지 jvm이 동시에 있을 때 기본적으로는 jre쪽 jvm을 사용하여 자바가 실행되게 됩니다.
그러나 이클립스의 몇몇 플러그인은 jdk쪽 jvm을 요구합니다. 대표적으로 Maven2 플러그인이 그렇습니다.
따라서 이클립스를 jdk의 jvm으로 구동하여야 합니다. 다음의 문구를 이클립스 메인 디렉토리의 eclipse.ini 파일을 열어 붙여넣습니다.
-showsplash
org.eclipse.platform
-vm
D:\Java\jdk1.5.0_14\jre\bin\client\jvm.dll
...하단내용...
이제부터가 중요한데 이클립스에서는 -vmargs 밑에 vm 옵션을 줌으로써 vm의 성능을 개선할 수 있습니다.
그럼 -vmargs 밑에 vm 옵션을 하나씩 붙여보겠습니다.
-Xverify:none 옵션은 바이트 코드에 대한 검증을 하지 않음으로써 성능이 좀 더 올라가게 됩니다.(그러나 옛날 옵션. 게다가 바이트 코드 검증을 제대로 안함으로써 발생하는 무한 루프의 지옥이 기다리고 있을지도...)
Sun의 jvm에서는 일반적인 GC 외에도 성능을 향상시켜주는 옵션을 제공하고 있습니다.
ParallelGC를 사용하면 좀더 쾌적한 개발환경을 꾸밀 수 있으며 1.5.0 update 6 버전부터는 -XX:+UseParallelOldGC란 옵션을 통해 full gc시에도 parallel 기능을 제공하고 있습니다. (요새 컴퓨터들은 하이퍼쓰레딩을 넘어 멀티 코어를 지원하고 있으니 사용하는 것이 좋습니다.)
Parallel Old GC까지 추가한 내용의 eclipse.ini는 다음과 같이 됩니다.
-showsplash
-vm
D:\Java\jdk1.5.0_14\jre\bin\client\jvm.dll
-vmargs
-XX:+UseParallelOldGC
...하단내용...
JVM은 -Xmx와 -Xms 옵션을 통해 최대 Heap Memory의 크기를 지정할 수 있도록 되어 있습니다.
그리고 이 전체 Heap은 객체의 생존 주기에 따라 크게 2가지 영역으로 나뉘게 됩니다. (http://blog.kangwoo.kr/51 이 글에도 댓글을 달았지만 Sun에서 제공하는 JVM heap 옵션들은 spec. 문서에 적혀있는 것과는 다르게 young + tenured 영역을 의미합니다. JVM whitepaper, spec. 문서에서는 아래 3영역이 모두 heap이란 이름으로 뭉뚱그러져 있습니다.)
Young Generation
새로 생성된 자바 객체가 위치하게 되는 부분입니다. 이 부분은 다시 Eden과 Servivor Spaces (1,2)라고 부르는 3개의 영역으로 나뉘게 됩니다. 금방 사라질 객체는 이 Eden 영역에서 바로 사라지게 되며 이 사라지게 되는 작업을 Minor Garbage Collection 이라고 부릅니다. 보통 GC라고 부릅니다. Eden 영역에서 살아남은 객체는 SS1, SS2 영역으로 옮겨지게 되며 이 두 영역이 가득차게 되면 Tenured라고 부르는 영역으로 넘어가게 됩니다. 제가 gc로그를 확인한 바로는 이 작업과 동시에 Full GC가 이루어지게 됩니다.
이 영역을 조절하는 옵션으로 -XX:NewRatio, -XX:NewSize, -XX:MaxNewSize가 있습니다.
-XX:NewSize=2m 보통 이 옵션을 주지 않으면 VM은 2메가를 기본 설정으로 사용하게 됩니다.
-XX:MaxNewSize 이 옵션은 Young 영역의 최대 크기를 지정합니다. Xmx 값 - MaxNewSize 값 - MaxPermSize 값 = Tenured 영역의 크기가 됩니다. 이 부분의 값이 커지면 Minor GC가 굉장히 자주 일어나게 되므로 이클립스 같은 client application을 다룰 경우 작은 값이 유리합니다. 보통 Xmx를 512로 잡을때 32로 잡으면 좋습니다.
Tenured Generation (1.4.x 까지는 이 영역을 Old Generation이라고 불렀습니다.)
Young Gen 영역에서 살아남은 객체들은 영속성을 부여받아 오랜시간 동안 이 영역에서 존재하게 됩니다.
이 영역을 조절하는 옵션으로 -XX:NewRatio가 있습니다.
-XX:NewRatio 이 옵션은 young과 tenured 영역을 비율로 나누게 됩니다. 예를 들어 3이란 값을 주었을 때 young:tenured=1:3 의 비율로 나누어지게 됩니다. 즉, Xmx 값 - MaxPermSize 값 한 결과에서 n+1로 공간을 나누게 되는 것입니다. 예를 들어 Xmx가 512이고 MaxPermSize가 128일 때 남은 공간은 396이고 비율이 12일 때 young 영역은 33m이, tenured 영역은 363m이 잡히게 됩니다. 이는 이클립스 같은 client application을 다룰 경우 tenured 영역이 커야하는 조건을 충족하는 옵션값이 될 것입니다.
Permanent Generation
이 영역은 위의 두 영역을 제외한 별도의 heap 공간으로서 자바의 바이트코드가 상주하게 되는 영역입니다. 아무런 옵션을 주지 않을 경우 기본 최대값인 64m까지 사용할 수 있습니다.
이 영역을 조절하는 옵션으로 -XX:PermSize와 -XX:MaxPermSize가 있습니다.
-XX:PermSize 이 영역의 처음 크기를 설정합니다.
-XX:MaxPermSize 이 영역의 최대 크기를 설정합니다.
이 Permanent Generation을 제외하고 나머지 영역을 합친 크기가 Xmx를 통해 설정한 크기와 동일하면 됩니다.
기본적으로 eclipse.ini에는 Xms가 40m, Xmx가 256m로 설정되어 있어 조금만 사용하다 보면 다운되기 일쑤입니다.
먼저 -XX:PermSize 와 -XX:MaxPermSize 옵션을 순서대로 동일한 값을 주어 설정합니다.
그 뒤 예제로 -XX:NewRatio=12를 주어 young과 tenured 영역을 나누겠습니다.
다음은 총 heap이 512m를 사용할 수 있도록 설정한 최종적인 eclipse.ini의 모습입니다.
-showsplash
org.eclipse.platform
-vm
D:\Java\jdk1.5.0_14\jre\bin\client\jvm.dll
-vmargs
-XX:+UseParallelOldGC
-XX:PermSize=128m
-XX:MaxPermSize=128m
-XX:NewRatio=12
-Xmx512m
-Xms512m
이 때 Xmx와 Xms는 JVM 설정 특성상 항상 맨 마지막에 있어야 합니다.
VM GC 로깅
플러그인의 종류와 크기, 프로젝트의 크기 등 다양한 변수에 따라 GC의 값을 튜닝하여야 할 필요가 있습니다.
이 때 다음과 같이 옵션을 줌으로써 GC 로그를 파일로 저장할 수 있습니다. (보통 유닉스 계열에서는 tail 명령으로 변경 내용을 계속 추적하면서 볼 수 있지만, 윈도우에서는 따로 볼 수 없으므로 파일로 저장 후 그 파일을 다시 복사하여 열어보는 것이 좋습니다.)
-showsplash
org.eclipse.platform
-vm
D:\Java\jdk1.5.0_14\jre\bin\client\jvm.dll
-vmargs
-verbose:gc
-Xloggc:D:\Java\testlogs\umonEclipse_gcLog.prof
-XX:+PrintGCDetails
-XX:+PrintTenuringDistribution
-XX:+UseParallelOldGC
-XX:PermSize=128m
-XX:MaxPermSize=128m
-XX:NewRatio=12
-Xmx512m
-Xms512m
-XX:+PrintGCDetails 옵션은 -XX:+PrintGC 옵션에 Permenant 영역의 GC 상태를 포함하여 보여줍니다.
다음은 gc 로그 예제입니다.
413.002: [GC [PSYoungGen: 10064K->5149K(26880K)] 47387K->43526K(104000K), 0.0233397 secs]
413.025: [Full GC [PSYoungGen: 5149K->0K(26880K)] [ParOldGen: 38376K->38631K(77120K)] 43526K->38631K(104000K) [PSPermGen: 44265K->44250K(131072K)], 0.9042747 secs]
432.477: [GC [PSYoungGen: 13440K->1922K(26880K)] 52071K->40553K(104000K), 0.0143935 secs]
413.002: [GC [PSYoungGen: 10064K->5149K(26880K)] 47387K->43526K(104000K), 0.0233397 secs]
이 로그는 young 영역이 10064K->5149K로 GC되었으며 young의 현재 총 크기가 26880K라는 것을 알려줍니다.
그리고 전체 heap의 크기가 104000K 이며 47387K->43526K로 크기가 변화하였음을 알려줍니다.
이런 식으로 로그를 쭉 읽어 나가면 됩니다.물론 버전별로 로그 찍히는 모양은 조금씩 다르게 나옵니다.
그리고 만약 vm 옵션을 잘못주게 되면 옵션과 상관없이 전체 heap 크기가 이클립스 기본 설정 + JVM 기본 설정으로 뜨게 됩니다. 그리고 이는 '메모리 많이 잡아주었는데 왜 자꾸만 죽지' 하는 것에 대한 이유입니다. (옵션 위치를 -vmargs 밑에 주지 않을 경우 자꾸 이클립스가 죽습니다... 네 제가 그랬습니다. OTL)
그럼 Full GC가 일어난 로그를 보겠습니다.
413.025: [Full GC [PSYoungGen: 5149K->0K(26880K)] [ParOldGen: 38376K->38631K(77120K)] 43526K->38631K(104000K) [PSPermGen: 44265K->44250K(131072K)], 0.9042747 secs]
보통 young 영역이 더 이상 제거할 수 없을 정도로 가득 찼을 때 해당 객체들이 Tenured 영역으로 넘어가게 되고, 이 Tenured 영역을 정리하는 과정에서 Full GC가 발생합니다. 현재 young 영역이 5149K->0K로 변화하여 남아있던 모든 객체가 Tenured 영역으로 옮겨갔다는 것을 알 수 있습니다. 그리고 Tenured 영역에서 약간의 GC가 일어났음을 알 수 있습니다. 그래서 young + tenured의 전체 크기 104000K 에서 43526K->38631K로 young 영역에서 제거된 크기정도가 줄었음을 알 수 있구요. PermSize는 128m으로 잡혀있는데 몇십K 정도의 GC가 발생하였습니다. (128m이라고 설정했다고 128000K 이 실제로 잡힌다고 생각하는 순진한 분들은 없겠죠?) 이클립스의 경우 Full GC가 길어지면 어플리케이션이 멈추게 되는 이유가 되므로 원인을 빠르게 파악하는게 중요합니다.
이런 식으로 로그를 보면서 Full GC가 자주 일어난다면 여러가지 옵션을 바꾸어서 young과 tenured 영역을 조절해가면서 프로그램을 수행해보면 될 것입니다.
더 자세한 내용은 아래 링크...
http://java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html
http://blog.naver.com/bigdra?Redirect=Log&logNo=140002602592
http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp
마지막으로
원래 이 글은 꽤 오래 전에 혼자만 맛있게 보려고 정리 했던 건데 위 링크에 있는 랑우(blog.kangwoo.kr)님의 글을 썬 레터를 받아보고 접했다 댓글을 달면서 이 글의 오류도 수정할 겸 해서 오픈을 하게 되었습니다. 음 제가 블로그를 거의 안 쓰고 띄엄띄엄 쓰느라 트랙백을 걸 줄 알면 좋은데 그냥 본문 안에 링크를 달 수 밖에 없었네요. 많은 분들이 JVM 메모리에 대한 이해가 높아지길 바랍니다.
그 외
아, 한가지 더. 이클립스가 자주 뻗는 분들은 이런 문제가 있지 않나 확인해보세요.