시스템이 내려간 원인의 단서를 찾을 때 로그를 분석해야 한다
(로그를 분석할 땐 application process가 살아있지 않아도 된다)
따라서, 모든 application에는 두가지를 설정해야 한다
GC로깅은 오버헤드가 거의 없으므로 JVM 프로세스는 항상 로깅을 켜놔야 한다
자바, JVM 애플리케이션이라면 무조건! 켜야 하는 필수 GC 로깅 플래그
아래와 같이 세세하게 남겨도 JVM성능에 영향을 주지는 않는다
-Xloggc:gc.log: GC 이벤트에 로깅할 파일을 지정한다-XX:+PrintGCDetails: GC 이벤트 세부 정보를 로깅한다-XX:+PrintTenuringDistribution: 툴링에 꼭 필요한, 부가적인 GC 이벤트 세부 정보를 추가한다-XX:+PrintGCTimeStamp: GC이벤트 발생 시간을 VM 시작 이후 경과한 시간을 초단위로 출력한다-XX:+PrintGCDateStamps: GC이벤트 발생 시간을 벽시계 시간 기준으로 출력한다verbose:gc(GC로그를 출력)는 지우고 대신 PrintGCDetails를 사용한다PrintGCTimeStamp, PrintGCDateStamps 는 둘다 필요하다. 전자는 GC 이벤트와 Application 이벤트를, 후자는 GC와 다른 내부 JVM 이벤트를 각각 연관짓는 용도이다로그 순환(Log Rotation) : 일정 단위로 로그파일을 재갱신하는 작업
-XX:+UseGCLogFileRotation: 로그 순환기능을 켬-XX:+NumberOfGCLogFiles=<n>: 보관 가능한 최대 로그파일 갯수를 설정한다-XX:+GCLogFileSize=<size>: 순환 직전 각 파일의 최대 크기를 설정한다GC로그와 달리, JMX는 GC에 영향을 준다
JXM는 런타임을 샘플링 하여 현재 상태를 업데이트 받는다
"런타임을 샘플링한다"
➡️ 애플리케이션의 실행 중인 상태를 주기적으로 확인하고 그 순간의 정보를 수집하는 과정을 의미
GC가 언제 실행될 지 알 수 없기 때문에 각 수집 사이클 전/후 메모리 상태를 알 수 없다. 따라서 GC 데이터를 깊이 있게 분석할 수 없다.
처음엔 GC 로깅을 JVM 구현체 디버깅 용도로 추가했다
거의 60개의 GC 플래그가 존재하는데, 이 플래그로 생성된 데이터 상당수가 성능 디버깅 목적으로 사용되었다.
➡️ 런타임에서 무슨 일이 발생했는지 정확히 파악하는데 GC 로깅이 아주 유용하다
GC 로깅은 핫스팟 JVM 내부에서 논블로킹 쓰기 매커니즘을 이용한다. Application 성능에 미치는 영향은 거의 0이므로 운영계에선 항상 켜두어야 한다
non-blocking?
특정 작업을 실행하는 동안 해당 작업이 완료될 때까지 다른 작업의 진행을 막지 않는 방식. 이는 다른 작업이 현재 진행 중인 작업의 완료를 기다리지 않고 동시에 진행될 수 있도록 함으로써, 전체 시스템의 처리량을 향상시키고 응답성을 높이는 데 도움을 준다
GC 로그 메세지는 따로 표준 포맷이 없다.
출력되는 로그는 매우 복잡하다.
➡️ 따라서 GC 로그를 직접 파싱하려 하지말고, 반드시 툴을 사용하자!
자동 분석 기능에서는 아래와 같은 기능을 제공한다
GC는 언제 튜닝해야 할까?
다음과 같은 사실을 기억하자!
저렴하다..?
➡️ 적은 비용으로 큰 효과를 나타낸다?
튜닝에서 중요한 인자
GC 힙 크기 조정 플래그
-Xms<size>: 힙 메모리의 최소 크기를 설정한다-Xmx<size>: 힙 메모리의 최대 크기를 설정한다-XX:MaxPermSize=<size>: 펌젠(Permgen) 메모리의 최대 크기를 설정한다(java 7 이전)-XX:MaxMetaspaceSize=<size>: 메타스페이스 메모리의 최대 크기를 설정한다 (java 8 이후)(참고) 펌젠과 메타스페이스
튜닝 시 주의할 것
➡️ 3가지 조건이 다 맞다면 GC가 성능 이슈를 일으키고 있을 가능성이 크다!
GC가 문제라면 어떻게 해결해야 하는가?
➡️ 할당과 중단 시간 양상을 파악해야 한다
작가의 경험 상, 할당률의 수치가 1GB/s 이상으로 일정 시간 지속한다면 GC 튜닝만으로 해결할 수 없는 성능 문제이다
➡️ 이럴 경우 application 에서 해당 로직을 제거하는 리팩토링을 실행하여 메모리 효율을 개선해야 한다
Parallel GC는 가장 단순한 수집기라 튜닝 역시 제일 쉽다
최소한의 튜닝만으로 충분하다
Parallel GC 특성
부분수집이란?
힙 메모리 전체를 대상으로 쓰레기를 수집하지 않고, 힙의 일부 영역만을 대상으로 가비지 컬렉션을 수행하는 과정
CMS는 동시수집기로서, 튜닝이 까다롭다
CMS는 정말로 STW 중단 시간을 단축시켜야 하는 유스케이스에 한해 어쩔 수 없을 때만 사용해야 한다
CMS에서 STW는 두단계에서 발생한다
1. 초기마킹: GC 루트가 직접 가리키는 내부 노드를 마킹한다
2. 재마킹: 카드 테이블을 이용해 조정 작업이 필요한 객체를 식별한다
따라서 CMS가 한번 일어날 때마다 반드시 2회 멈춘다.
savepoint에 예민한 저지연 애플리케이션에서는 중요한 영향을 미칠 수 있다
그래서 튜닝을 어떻게?
CMS 수집 시, 코어 절반은 GC에 할당된다
CMF 발생 직전 수집기 상태를 살펴봐라
CMF 란?
Concurrent Mode Failure는 CMS 가비지 컬렉터가 Old Generation에서 가비지를 수집하는 동안, 동시에 진행되는 애플리케이션의 객체 할당 속도가 가비지 컬렉터가 메모리를 회수할 수 있는 속도를 초과할 때 발생
CMS 수집이 끝나자마자 새 CMS 수집이 시작되는 백투백 수집현상은 동시 수집기가 얼마 못 가 고장날 것 이고, CMF가 발생할 것이다
튜닝 시, 이런 현상이 발생해도 괜찮은지 확인해봐야 한다
혹은 CMS 진행 시, GC에 할당된 코어 수를 줄이는 방법도 있다
CMF가 발생할지 미리 알 수 없을까?
➡️-XX:PrintFLSStatistics=1 JVM 스위치를 추가하여 로그에 표시된 지표를 확인하자
G1은 튜닝을 해도 어떤 영향을 미칠지 알아차리기가 어렵다
그래도 옵션을 지정해서 튜닝해야 할 경우
-XX:+UnlockExperimentalVMOptions 스위치를 지정하기!
-XX:G1NewSizePercent=<n>, -XX:G1MaxNewSizePercent=<n> 을 사용할 경우 꼭 필요하다
JVM이 연속적으로 실행되지 못한 지점(hiccup, 딸꾹질)을 보여주는 도구 이다
hiccup이 발생하는 이유
GC STW, OS나 플랫폼 관련 문제