Garbage Collection – 2부

1. GC 발생 시나리오

객체가 생성되면 Eden 영역에 위치 하게 된다.

Eden영역이 가득차게 되면 Minor GC가 발생하여 참조가 없는 객체는 삭제되고, 참조 중인 객체는 Suvvivor 영역으로 이동한다.

Survivor영역이 가득차게 되면 Minor GC가 발생하고 참조가 없는 객체는 삭제되고, 참조 중인 객체는 다른 Suvvivor 영역으로 이동한다.

Survivor영역에서의 GC과정을 반복 하며, 계속 참조 중인 객체는 OLD 영역으로 이동한다.

Eden 영역에서 Survivor 영역으로 이동 할 때 객체가 남아있는 영역보다 큰 경우 OLD 영역으로 이동한다.


2. GC 방식

2.1 Serial Collector

Young / Old Generation 모두 Serial로 Single CPU를 사용합니다.

즉 1 개의 스레드를 가지고 GC를 수행합니다.

Client VM의 기본 컬렉터이며 현재는 거의 사용되지 않는 컬렉터입니다.

2.2 Parallel Collector

이 방식은 throughput collector로도 알려진 방식입니다.

이 방식의 목표는 다른 CPU가 대기 상태로 남아 있는 것을 최소화하는 것입니다.

시리얼 콜렉터와 달리 Young 영역에서의 콜렉션을 병렬(Parallel)로 처리합니다.

많은 CPU 를 사용하기 때문에 GC의 부하를 줄이고 애플리케이션의 처리량을 증가시킬 수 있습니다.

이는 다수의 스레드가 동시에 GC를 수행하며 적용범위는 Young Area에 국한됩니다.

Old Area는 Serial Mark-Sweep-Compact 알고리즘이 사용됩니다.

이 컬렉터는 Server VM의 JVM에서 기본 Collector이며 Client VM에서도 옵션 설정으로 사용이 가능합니다.

단 CPU가 1개라면 해당 옵션은 무시됩니다.

2.3 Concurrent Mark-Sweep (CMS) Collector

초기 Initial Mark 단계에서는 클래스 로더에서 가장 가까운 객체 중 살아 있는 객체만 찾는 것으로 마무리 됩니다..

따라서, 멈추는 시간은 매우 짧습니다.

그리고 Concurrent Mark 단계에서는 방금 살아있다고 확인한 객체에서 참조하고 있는 객체들을 따라가면서 확인합니다.

이 단계의 특징은 다른 스레드가 실행 중인 상태에서 동시에 진행된다는 것입니다.

그 다음 Remark 단계에서는 Concurrent Mark 단계에서 새로 추가되거나 참조가 끊긴 객체를 확인합니다.

마지막으로 Concurrent Sweep 단계에서는 쓰레기를 정리하는 작업을 실행합니다.

이 작업도 다른 스레드가 실행되고 있는 상황에서 진행합니다.

이러한 단계로 진행되는 GC 방식이기 때문에 stop-the-world 시간이 매우 짧습니다.

모든 애플리케이션의 응답 속도가 매우 중요할 때 CMS GC를 사용하며, Low Latency GC라고도 부릅니다.

그런데 CMS GC는 stop-the-world 시간이 짧다는 장점에 반해 다음과 같은 단점이 존재합니다.

  • 다른 GC 방식보다 메모리와 CPU를 더 많이 사용한다.
  • Compaction 단계가 기본적으로 제공되지 않는다.

따라서, CMS GC를 사용할 때에는 신중히 검토한 후에 사용해야 합니다.

그리고 조각난 메모리가 많아 Compaction 작업을 실행하면 다른 GC 방식의 stop-the-world 시간보다 stop-the-world 시간이 더 길기 때문에 Compaction 작업이 얼마나 자주, 오랫동안 수행되는지 확인해야 합니다.

3. JVM OPTION

종류옵션
동작 모드-sever
전체 힙 크기-Xms와 -Xmx의 값을 같게
New 영역 크기-XX:NewRatio 2~4 정도의 값
 -XX:NewSize=?
-XX:MaxNewSize=?
NewRatio 대신 NewSize를 지정하는 것도 좋다.
Perm 크기-XX:PermSize=256m
-XX:MaxPermSize=256m
성능에 영향을 미치지 않으므로 동작에 문제가 없을 정도만 지정한다.
GC 로그-Xloggc:$CATALINA_BASE/logs/gc.log
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
GC 로그를 남기는 것은 특별히 Java 애플리케이션 수행 성능에 영향을 미치지 않는다.
가급적이면 GC 로그를 남기는 것이 좋다.
GC 알고리즘-XX:+UseParNewGC
-XX:+CMSParallelRemarkEnabled
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
일반적으로 권할만한 설정일 뿐이다.
애플리케이션 특성에 다라 다른 선택이 더 좋을 수 있다.
OOM 에러 발생 시
힙 덤프 생성
-XX:+HeapDumpOnOutOfMemoryError
-XX:+HeapDumpPath=$CATALINA_BASE/logs
OOM 발생 이후 조치-XX:OnOutOfMemoryError=$CATALINA_HOME/bin/stop.sh 또는
-XX:OnOutOfMemoryError=$CATALINA_HOME/bin/restart.sh 힙 덤프를 남긴 뒤,
관리 정책에 맞게 적합한 동작을 취할 수 있도록 한다.

4. 분석

GC 옵션을 적용하고, -verbosegc 옵션을 지정한 다음에 tail 명령어로 로그가 제대로 쌓이고 있는지 확인해야 합니다.

로그가 잘 쌓이고 있다면, 하루 혹은 이틀 정도의 데이터가 축적된 후 결과를 확인해 봅니다.

로그를 로컬 PC로 옮긴 다음에 HPJMeter로 분석하는 것이 가장 쉽습니다.

분석할 때에는 다음의 사항을 중심으로 살펴보는 것이 좋습니다.

순서는 필자(God of Java 이상민님) 나름의 기준에 따른 우선 순위입니다.

GC 옵션을 결정하는 데 가장 큰 비중을 차지하는 것은 Full GC 수행 시간입니다.

  • Full GC 수행시간
  • Minor GC 수행시간
  • Full GC 수행간격
  • Minor GC 수행간격
  • 전체 Full GC 수행시간
  • 전체 Minor GC 수행시간
  • 전체 GC 수행시간
  • Full GC 수행횟수
  • Minor GC 수행횟수

운이 좋아서 한 번에 가장 적합한 GC 옵션을 찾으면 좋지만, 그렇지 못한 경우가 대부분입니다.

한 번에 끝내려다가 잘못하면 서비스에 OutOfMemoryError가 발생할 수 있으니 조심해서 GC 튜닝을 진행 해야 합니다.

참고사이트

Leave a Comment