19. GC 튜닝을 항상 할 필요는 없다

GC 튜닝이 필요 없다는 이야기는 운영 중인 Java 기반 시스템의 옵션에 기본적으로 다음과 같은 것들은 추가되어 있을 때의 경우다.

그리고 시스템의 로그에는 다음과 같은 타임아웃 관련 로그가 남아있지 않아야 한다.

그래서 JVM의 메모리 크기도 지정하지 않았고, Timeout이 지속적으로 발생하고 있다면 시스템에서 GC 튜닝을 하는 것이 좋다. 그런데 명심할 것은 GC 튜닝은 가장 마지막에 하는 작업이라는 것이다.

Old 영역으로 넘어가는 객체의 수 최소화하기

Oracle JVM에서 제공하는 모든 GC는 Generational GC이다. 즉 Eden 영역에서 객체가 처음 만들어지고 Survivor 영역을 오가다가 끝까지 남아 있는 객체는 Old 영역으로 이동한다. 간혹 Eden 영역에서 만들어지다가 크기가 커져서 Old 영역으로 바로 넘어가는 객체도 있긴 하다. Old 영역의 GC는 New 영역의 GC에 비하여 상대적으로 시간이 오래 소요되기 때문에 Old 영역으로 이동하는 객체의 수를 줄이면 Full GC가 발생하는 빈도를 많이 줄일 수 있다.

Full GC 시간 줄이기

Full GC의 수행 시간은 상대적으로 Young GC에 비하여 길다. 그래서 Full GC 실행에 시간이 오래 소요되면(1초 이상) 연계된 여러 부분에서 타임아웃이 발생할 수 있다. 그렇다고 Full GC 실행 시간을 줄이기 위해 Old 영역의 크기를 줄이면 OutOfMemoryError가 발생하거나 Full GC 횟수가 늘어난다. 반대로 Old 영역의 크기를 늘리면 Full GC 횟수는 줄어들지만 실행 시간이 늘어난다. Old 영역의 크기를 적절하게 ‘잘’ 설정해야 한다.

GC의 성능을 결정하는 옵션들

구분 옵션 설명
힙(heap) 영역 크기 -Xms JVM 시작 시 힙 영역 크기
힙(heap) 영역 크기 -Xmx 최대 힙 영역 크기
New 영역 크기 -XX:NewRatio New 영역과 Old 영역의 비율
New 영역 크기 -XX:NewSize New 영역의 크기
New 영역 크기 -XX:SurvivorRatio Eden 영역과 Survivor 영역의 비율

GC 의 성능에 많은 영향을 주는 또 다른 옵션은 GC 방식이다.

구분 옵션
Serial GC -XX:+UseSerialGC
Parallel GC -XX:+UseParallelGC; -XX:ParallelGCTHreads=value
Parallel Compacting GC -XX:+UseParallelOldGC
CMS GC -XX:+UseConcMarkSweepGC; -XX:+UseParNewGC; -XX:+CMSParallelRemarkEnabled; -XX:CMSInitiatingOccupancyFraction=value; -XX:+UseCMSInitiatingOccupancyOnly
G1 -XX:+UnlockExperimentalVMOptions; -XX:+UseG1GC

G1 GC를 제외하고는 각 GC 방식의 첫 번째 줄에 있는 옵션을 지정하면 GC 방식이 변경된다. Serial GC는 클라이언트 장비에 최적화되어 있기 때문에 특별히 신경쓸 필요가 없다.

GC 튜닝 절차

  1. GC 상황 모니터링
  2. 모니터링 결과 분석 후 GC 튜닝 여부 결정
    분석 결과 GC 수행에 소요된 시간이 0.1~0.3초 밖에 안된다면 굳이 튜닝할 필요 없다. 하지만 1~3초, 심지어 10초 이상 걸리면 GC 튜닝을 진행해야 한다.
  3. GC 방식/메모리 크기 지정
  4. 결과 분석
  5. 결과가 만족스러울 경우 전체 서버에 반영 및 종료