달력

32024  이전 다음

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

[펌]가비지 콜렉션

JAVA/JSE 2005. 10. 12. 10:40

가비지 콜렉션

2004년 1월의 테크팁이었던 Monitoring Class Loading and Garbage Collection에서는 자바 커맨드 라인 툴 (자바 애플리케이션 런쳐)를 이용하여 커맨드 라인 옵션인 -verbose:gc에 대해서 알아보았다. 표준이 아니라는 이유로 -XX 옵션에 대해 신경 쓰지 않는다거나, 옵션의 균질성을 중요시한다거나, 또는 현재 사용하고 있는 옵션에 대해서 알고 싶다면 다음과 같이 -verbose:gc 를 입력해보자.

     -XX:+PrintGC -XX:+TraceClassUnloading

이것은 -verbose:gc 옵션이 어떻게 번역되는지를 보여준다.

이 옵션은 애플리케이션이 실행되는 동안 가비지 콜렉션 이벤트의 리포트를 요구한다. J2SE v1.4.2에서는 가비지 콜렉터를 컨트롤하는 다른 많은 커맨드 라인이 있다. 코드의 어느 라인도 변경하지 않고 애플리케이션의 성능을 최대한으로 활용하려면, 이 옵션들을 최소한 하나 이상 사용할 수가 있다. 이번 팁에서는 가비지 콜렉터를 컨트롤하는 부수적인 많은 자바 커맨드 라인을 다룰 것이지만, 가비지 콜렉션 튜닝 옵션의 전체를 말하지는 않는다.

자바 애플리케이션 런쳐는 표준 커맨드-라인 스위치와 비표준 커맨드-라인 스위치를 모두 동반한다. -verbose:gc 옵션에 관한 정보에 대해 알아보려면, 특정 플랫폼별로 다음 웹페이지를 참고하자.

위의 참고 페이지에는 몇몇 비표준 옵션들에 대해서도 나와있다. 자바 커맨드 라인 툴에서 표준 옵션인 -X을 실행하면 사용자의 플랫폼을 위한 비표준 옵션을 보여준다.

솔라리스 플랫폼이라면, java -X 을 실행했을 경우 출력되는 내용은 다음과 같다.

   -Xmixed           mixed mode execution (default)   -Xint             interpreted mode execution only   -Xbootclasspath:<directories and zip/jar files separated by :>                     set search path for bootstrap classes and                      resources   -Xbootclasspath/a:<directories and zip/jar files separated by :>                     append to end of bootstrap class path   -Xbootclasspath/p:<directories and zip/jar files separated by :>                     prepend in front of bootstrap class path   -Xnoclassgc       disable class garbage collection   -Xincgc           enable incremental garbage collection   -Xloggc:<file>    log GC status to a file with time stamps   -Xbatch           disable background compilation   -Xms<size>        set initial Java heap size   -Xmx<size>        set maximum Java heap size   -Xss<size>        set java thread stack size   -Xprof            output cpu profiling data   -Xrunhprof[:help]|[:<option>=<value>, ...]                     perform JVMPI heap, cpu, or monitor profiling   -Xdebug           enable remote debugging   -Xfuture          enable strictest checks, anticipating future                      default   -Xrs              reduce use of OS signals by Java/VM (see                      documentation)   -Xcheck:jni       perform additional checks for JNI functions     The -X options are non-standard and subject to change without   notice.

위의 출력값의 마지막 줄을 주목하자. 그러한 비표준 옵션들을 사용하는 것은 사용자의 책임 하에 있음을 분명히 나타내고 있다.

가비지 콜렉션을 위한 세 가지 특정한 비표준 옵션은 -Xnoclassgc, -Xincgc, 그리고 -Xloggc:<file>이다. -Xnoclassgc 옵션을 지정한다면, 콜렉션이 발생하지만 클래스들은 permanent generation으로부터 수집되지 않는다. 이는 콜렉션이 수거하는 메모리가 없음을 의미한다. 만약 클래스를 로딩하려고 할 때 이미 로딩된 클래스들을 위해 메모리를 써버렸다면 콜렉션은 문제를 해결하지 않을 것이다.

썬의 1.4.2 구현에서 -Xincgc-XX:+UseTrainGC 옵션과 동등한 값을 가진다. 즉, -Xincgc은 old generation을 위해서 디폴트 값에서 주어진 "serial" 콜렉터를 사용하기 보다는 "train"콜렉터를 사용한다. ("generations"의 개념은 이번 팁의 후반에서 다루게된다.) train 콜렉터는 약 10%의 퍼포먼스 부하를 발생시킨다. 또한 old generation 전체를 한 번에 수집할 수 없기 때문에 약간의 공간적인 부하(space overhead)도 발생한다. train 콜렉터는 점진적으로 작동하는 장점을 가지기 때문에 휴지 시간이 상당히 짧다. J2SE 1.5.0 베타 릴리즈에서는 -Xincgc 플래그는 train 콜렉터보다는 CMS(concurrent mark sweep) 콜렉터를 인보킹하게 된다. 그 이유는, CMS 콜렉터가 train 콜렉터보다 더 균일하기 때문이다.

-Xloggc 옵션을 지정해줌으로써 -verbose:gc 출력값의 등가를 파일로 출력할 수 있다. -verbose:gc 에 의한 출력값과 비교해 보면, -Xloggc 에 의한 출력값은 한 가지 추가적인 항목을 포함하는데, 그것은 애플리케이션에서의 첫 콜렉션이 일어난 시간에서부터 가비지 콜렉션이 발생한 시간에 대한 정보이다. 다음의 -Xloggc 샘플 출력값에서 살펴보자.

   0.000: [GC 512K->153K(1984K), 0.0198483 secs]   0.372: [GC 506K->281K(1984K), 0.0206428 secs]   0.393: [Full GC 281K->281K(1984K), 0.0888926 secs]   0.519: [GC 947K->941K(1984K), 0.0045715 secs]   0.524: [Full GC 941K->941K(1984K), 0.0797666 secs]   0.650: [GC 2107K->1597K(2808K), 0.0013546 secs]   0.838: [GC 2107K->1700K(2808K), 0.0116557 secs

파일에 재전송할 필요없이 -XX:+PrintGCTimeStamps 옵션을 지정해줌으로써 -Xloggc 출력값에 시간 소인을 나타낼 수 있다.

-Xnoclassgc, -Xincgc-Xloggc:<file> 이외에도 가비지 콜렉터를 컨트롤하는 다른 옵션들이 존재한다. 예를 들자면, 콜렉터가 실행할 때 간접적으로 영향을 끼치도록 -Xms -Xmx 옵션을 사용하여 메모리 할당의 풀 사이즈를 변경할 수 있다. -Xms의 값을 크게 지정해주면, 그만큼 큰 자바 객체 힙으로 작업하게 된다. 이것은 힙을 채우는 데에 더 많은 시간이 걸린다는 것을 의미하고 결과적으로 콜렉션을 피하지는 않지만 미루게 된다는 것이다. 큰 -Xms 값은 또한 더 많은 시스템 리소스를 소모하게 된다. 반면 -Xmx의 값을 크게 지정하면, 자바 객체 힙의 사이즈가 필요 시에 커질 수 있게 된다. 이렇게 값이 큰 객체 힙은 조금 덜 빈번하게 가비지 콜렉트된다는 점 외에는 모든 점이 동일하다. 따라서 이 옵션을 통해 가비지 콜렉션이 빈번하지만 짧은 시간동안 일어나는 것과, 횟수는 적지만 오랜 시간동안 가비지 콜렉션이 일어나는 것 중 하나를 선택할 수 있다. 하지만 각각의 콜렉션이 부하를 발생하기 때문에, 사용자가 정의하는 요구조건에 따라 더 나은 접근법은 달라지게 되어있다.

-Xms-Xmx 외에도 가비지 콜렉션에 영향을 주는 비표준 옵션들이 존재한다. 그 옵션들은 java -X 도움말에서 볼 수 없는데, 그 이유는 이 옵션들은 비표준 옵션이기 때문에 두개의 X를 사용해야하기 때문이다.

만약 힙의 최소값과 최대값을 지정해주고자 한다면 (이 때, 디폴트 값의 범위는 40%~70%), -XX:MinHeapFreeRatio=<Minimum> 옵션과 -XX:MaxHeapFreeRatio=<Maximum> 옵션을 사용하면 된다. 그러나 최소값을 너무 작게 설정해놓으면, 힙은 콜렉션 후에 충분한 여유공간을 확보하지 못하게 되므로, 단기간 내에 또다시 콜렉팅을 해야할 수도 있다. 반면, 최소값을 크게 설정하면, 더 많은 "head room"이 생기므로 다음 콜렉션 때까지 시간을 지연시킬 수 있을 것이다. -XX:MinHeapFreeRatio 의 값을 크게 했을 때의 단점은 시스템 메모리가 이 가상 장치에 고정되기 때문에 사용자의 또 다른 애플리케이션에 사용될 수 없다는 점이다.

-XX 세트내의 다른 비표준 옵션들은 가비지 콜렉터의 작동 방법에 영향을 끼친다. -XX:+UseConcMarkSweepGC 커맨드 라인 플래그("concurrent mark sweep garbage collection"의 줄임말)는 병행(concurrent) 가비지 콜렉터를 실행시킨다. -XX:+UseConcMarkSweepGC 콜렉터는 old generation을 동시에 콜렉팅한다. 그 이유는 전반적인 콜렉션 같은 old generation 들은 콜랙팅할 때 대체적으로 시간이 오래 걸려서 병행 가비지 콜렉터를 실행시키는 것이 더 효과적이기 때문이다. 이는 애플리케이션에서 가비지 콜렉션 휴지 시간을 짧게 할 수 있도록 프로세싱 파워를 활용한다는 것을 말한다. -XX:+UseParallelGC 커맨드 라인 플래그는 병렬 가비지 콜렉팅을 가능하게 한다. -XX:+UseParallelGC 콜렉터는 오직 다중 프로세서를 이용한 young generation만을 콜렉팅한다. 이 접근법은 처리율은 높이지만, 전반적인 콜렉션의 휴지 시간을 줄이지는 못한다.

또 하나의 흥미로운 옵션인 -XX:+PrintGCDetails 는 각각의 콜렉션에서 각 generation에 어떤 일이 발생했는지를 보여준다. (단, permanent generation 에 대한 자세한 사항들은 보여주지 않는다. 이 점은 JDK 1.5.0.에서 수정되었다.)

SwingSet2의 데모에 사용된 -XX:+PrintGCDetails의 예제를 살펴보자. (출력값의 라인들은 테크팁 페이지의 경계선에 맞추기 위해 나누어주었다.)

   java -Xloggc:details.out -XX:+PrintGCDetails -jar    /home/eo86671/j2sdk1.4.2/demo/jfc/SwingSet2/SwingSet2.jar   0.000: [GC 0.000: [DefNew: 511K->64K(576K), 0.0182344 secs]         511K->153K(1984K), 0.0185255 secs]   1.387: [GC 1.387: [DefNew: 417K->64K(576K), 0.0192086 secs]        1.407: [Tenured: 217K->281K(1408K), 0.0725645 secs]         506K->281K(1984K), 0.0923346 secs]   1.559: [GC 1.559: [DefNew: 10K->3K(576K), 0.0044098 secs]        1.564: [Tenured: 937K->941K(1408K), 0.0741569 secs]        948K->941K(1984K), 0.0790573 secs]   1.703: [GC 1.703: [DefNew: 510K->0K(576K), 0.0011627 secs]         2107K->1597K(2808K), 0.0013820 secs]   2.210: [GC 2.210: [DefNew: 509K->64K(576K), 0.0112637 secs]         2107K->1710K(2808K), 0.0115942 secs]   2.927: [GC 2.927: [DefNew: 575K->64K(576K), 0.0170128 secs]        2222K->1841K(2808K), 0.0173293 secs]   8.430: [GC 8.430: [DefNew: 576K->64K(576K), 0.0142839 secs]         2353K->2025K(2808K), 0.0156266 secs]   8.823: [GC 8.823: [DefNew: 494K->64K(576K), 0.0164915 secs]         2456K->2243K(2808K), 0.0166323 secs]   8.856: [GC 8.856: [DefNew: 569K->41K(576K), 0.0058505 secs]        8.862: [Tenured: 2341K->1656K(2360K), 0.1464764 secs]         2749K->1656K(2936K), 0.1526133 secs]

변경내용이 그 결과에 어떤 영향 (예를 들어서, 콜렉션간의 시간 연장이나 각각 특정 휴지 시간의 감소)을 주는지를 알아보기 위해 -Xms 옵션과 -Xmx 옵션을 조정해보자.

앞서 'generations'의 힙 공간에 대해 언급한 바 있다. 이 말이 무슨 의미인지 궁금하다면, 여기 짤막한 설명을 참조하자. JRE 1.4.2 에서 힙은 young, old, 그리고 permanet와 같이 세 개의 generation으로 구분된다. young generation은 객체가 생성된 장소이다. 이 generation의 사이즈는 -XX:NewSize 옵션과 -XX:MaxNewSize 옵션에 의해 컨트롤된다. 새로운 객체를 보유하게 되면, old generation으로 승격된다. old generation의 사이즈는 전체 힙 사이즈 (-Xms-Xmx) 에서 young generation의 사이즈 (-XX:NewSize-XX:MaxNewSize)를 감한 사이즈로 계산된다. young generation은 명시적 크기보다는 -XX:NewRatio=를 이용하여 더 잘 지정할 수 있는데, -XX:NewRatio= 옵션은 힙 설정의 전체 사이즈를 설정해주기 때문이다. 선택한 사이즈는 콜렉팅 되는 시간에 대해 콜렉션의 횟수를 결정하게 된다. 어떤 애플리케이션들은 짧은 휴지 시간을 요구하는 한편, 콜렉터의 효율성을 필요로 하는 애플리케이션들도 있다. (많은 애플리케이션에서 이 단계의 튜닝을 해줄 필요는 전혀 없다.)

마지막으로 언급할 GC옵션은 -XX:+DisableExplicitGC이다. System.gc()의 호출을 이용하여 가비지 콜렉터를 실행하라는 명령을 했을 때, 시스템이 이를 무시하기를 원한다면 이 옵션을 사용해보자. 가비지 콜렉터는 프로그래머가 명령했을 때가 아닌, 필요시에 계속해서 실행되고 있을 것이다. 하지만 프로그래머의 명령을 무시하는 것이 좋은 생각일까? 아마도 아닐 것이다. 왜냐하면, 프로그래머가 가비지 콜렉터를 실행하기 적당한 시점이라고 생각할 수 있기 때문이다. 물론, 만약 공유 라이브러리를 사용하고 있고, 실행 콘텍스트가 프로그래머의 원래의 계획과 다르다면, 이러한 새로운 상황에 유효하지 않을 수도 있다.

가비지 콜렉션에 관련된 옵션들에 대해 좀더 자세한 정보를 원한다면, Tuning Garbage Collection with the 1.4.2 Java Virtual Machine를 읽어보기 바란다. 튜닝을 할 때는 튜닝의 목적이 성능 향상인지, 짧은 휴지 시간인지 아니면 작은 범위의 ㄴ메모리 사용을 위한 것인지 그 목적을 확실히 해야 한다. JVM이 업데이트 되면서 선택할 수 있는 튜닝 옵션들도 다시 한번 살펴 보아야 한다. 이번 팁에서 다룬 옵션들의 대부분은 썬의 런타임 환경 1.4.2 릴리즈에 한정됨을 기억하자. 1.5.0 릴리즈는 고유의 컨트롤 셋과 디폴트 셋을 가진다.

Posted by tornado
|