[Java] JVM Garbage Collection Tuning

몇 가지 기본적인 GC 튜닝 방법을 적어봅니다.

1. Concurrent Mark and Sweep 알고리즘에서 class unloading 문제

대충 아래와 같은 옵션으로 실행되는 자바 서버 프로세스가 있다고 가정하자.

-XX:+UseParNewGC -XX:ParallelGCThreads=4 -XX:+UseConcMarkSweepGC

이것은 old generation에 대해서 CMS 알고리즘을 사용하기 위한 튜닝 방법이다.
그런데 다음 내용을 보면 CMS GC를 사용할 때 기본값으로 class unloading 즉, perm gc를 안 하도록 되어 있다.

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6329603

해결책은 다음 옵션을 추가하는 것이다.

-XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled

2. 트랜잭션이 많고 해당 객체들의 lifespan은 짧을 경우의 튜닝 (Sun Hotspot JVM. HP JVM도 마찬가지)
  • 새로운 객체들이 많이 생성되고 또 트랜잭션양이 많은 경우 Thruput 중심의 garbage collector를 사용해야 하기 때문에 기본값인 UseParallelGC 옵션은 그냥 사용하면 될 것 같고..
  • Parallel GC Thread 갯수는 CPU 갯수와 동일하게 혹은 2배 정도로 증가시킬 필요가 있다. 예를 들어 CPU가 6장이라면
    -XX:ParallelGCThreads=12
  • eden 과 tenured의 비율을 지정하는 옵션인 NewRatio 옵션이 기본값인 2인데 eden이 계속 부족해질 가능성이 높으므로 NewRatio 값을 1 정도로 하여 eden과 tenured 비율이 같도록 하는 게 좋겠다. (1.5가 먹는지 모르겠음 ㅠ_ㅠ)
    -XX:NewRatio=1
  • 위의 옵션들은 주로 young generation의 처리 성능을 높여 thruput을 높이는 목적을 가지고 있는데 full gc 가 발생하면 old generation까지 collection 대상이 된다. 기본값은 old gen에 대해서는 serial collector 즉, single thread 방식이므로 pause time이 길 수 있는데 JDK 5.0update6 (SUN JVM 기준)부터는 parallel old gc 기능이 추가되었다. 이 기능을 사용하려면 다음 옵션을 추가해야 한다. 이 옵션은 young generation에 대해 UseParallelGC 옵션이 사용되는 경우에만 가능하다.
    -XX:+UseParallelOldGC
    HP JVM의 경우 Sun JDK 5.0u6에 해당하는 JVM 버전은 5.0update4이다.
  • 그외 jconsole을 써서 모니터링할 수 있으려면 해당 JVM을 실행할 때 다음 옵션을 켜줘야 한다.
    -Dcom.sun.management.jmxremote
  • 메모리 문제(OutOfMemoryError)가 발생할 경우 heap 분석을 위해서는 다음 옵션 추가하는 것 빼먹지 말아야 한다. -XX:+HeapDumpOnOutOfMemoryError

3. IBM JVM의 경우 튜닝

IBM JVM 은 gc 특성이 Sun/HP와는 완전히 다르다. -verbosegc 옵션을 켜면 기본값으로 standard error로 gc log를 내보내는데 verbose gc 로그의 timestamp는 gc가 시작한 시간을 나타낸다. IBM JDK는 gc 때에는 무조건 stop the world 즉, 다른 일을 할 수 없는 상태가 된다. (이런 면에서는 Sun의 HotSpot에 비해 뒤처진다고 보여짐.) gc log에서 <sys 라고 되어 있는 부분은 System.gc 가 호출된 것이고 <af 라고 되어 있는 부분은 allocation failure 즉, 메모리 부족에 의해 garbage collection이 trigger되는 경우이다. 자세한 것은 IBM JDK diagnosis 문서 참조.

  • verbose gc log를 다른 파일로 쓰려면 다음 옵션을 추가
    -Xverbosegclog:<파일경로>
  • GC 정책을 지정하는 옵션은 다음과 같다.
    -Xgcpolicy:<정책 이름>
  • 위의 정책 이름 중 가능한 것은 다음과 같다.
    optthruput : 기본값. 이 알고리즘은 gc pause time이 긴 게 흠이다. mark and sweep 시 mark 하는 부분이 single thread로밖에 실행할 수 없어 가끔씩 매우 긴 pause time이 발생한다.
    optavgpause : heap이 클 경우에 gc pause time을 줄이기 위해 사용하는 옵션
    gencon : gc pause time을 최소화하는 옵션
    subpool : 성능적으로 더 우수한 object allocation 알고리즘을 사용. 큰 SMP 시스템에 유용(CPU 16장 등...) AIX, Linux PPC, zSeries, z/OS, i5/OS 에만 사용 가능하며 이 알고리즘에서는 LOA(large object area)를 사용하지 않는다.
    보통 기본값인 optthruput이 적용되어 있으니, pause time이 너무 길다면 optavgpause 나 gencon을 사용하는 게 좋겠다.
  • GC 쓰레드 갯수는 다음 옵션을 사용하여 지정한다.
    -Xgcthreads<쓰레드 갯수>
    기본값으로 CPU 갯수만큼 사용하게 되어 있다. 필요하다면 CPU 갯수의 2배 정도로 쓰레드 갯수를 지정하면 좀더 효율이 높을 것이다.
  • LOA / SOA 비율 지정
    -Xloainitial<퍼센트>, -Xloamaximum<퍼센트>
    IBM JVM에서 tenured 영역은 soa(small object area)와 loa로 구분된다. 그 초기 및 최대 비율을 LOA : SOA로 지정한다. 기본값은 초기는 0.05 (혹은 5%)이고, 최대는 0.5 (혹은 50%)이다. 0과 0.95 사이의 값을 지정해야 한다. LOA를 많이 사용하지 않는다면 기본값을 그대로 사용하면 된다.
  • 예를 들자면 다음과 같이 지정할 수 있다.
    -verbose:gc -Xverbosegclog:<파일경로> -Xgcpolicy:optavgpause -Xgcthreads8

4. 참고 자료
추가 (2013-03-29)
Hotspot JVM은 JDK 6에서는 큰 변화가 없었네요.
  • Hotspot JVM Tuning Guide (JDK 6.0 기준)
    • http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html
IBM JDK에 추가된 gencon은 Hotspot에 이미 구현된  generatioinal collector 인데 IBM JDK 7에서는 gencon을 기본값으로 권장하고 있다.  이 경우 hotspot JVM의 경우처럼 new area의 크기를 지정해줘야 한다.  -Xmns 는 new area의 초기값,  -Xmnx는 new area의 최대값이다. 32bit JVM은 256MB - 512MB, 64bit JVM은 512MB - 1024MB 사이의 크기가 가능하다. 64bit JVM에서 32bit 방식의 addressing을 사용하는 옵션은 -XcompressedRefs 를 켜면 훨씬 효율적인 성능을 보이는데 이 경우 최대 힙 크기는 28GB로 제약된다.

댓글