Java 최적화 - Chpater8

이유진·2024년 2월 25일
post-thumbnail

GC 로깅, 모니터링, 튜닝, 툴

시스템이 내려간 원인의 단서를 찾을 때 로그를 분석해야 한다
(로그를 분석할 땐 application process가 살아있지 않아도 된다)

따라서, 모든 application에는 두가지를 설정해야 한다

  • GC 로그를 생성한다
  • 애플리케이션 출력과는 별도로 특정 파일에 GC로그를 보관한다

GC로깅은 오버헤드가 거의 없으므로 JVM 프로세스는 항상 로깅을 켜놔야 한다

GC 로깅 켜기

자바, 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 로그 vs JMX

GC로그와 달리, JMX는 GC에 영향을 준다

JMX의 단점

JXM는 런타임을 샘플링 하여 현재 상태를 업데이트 받는다

"런타임을 샘플링한다"
➡️ 애플리케이션의 실행 중인 상태를 주기적으로 확인하고 그 순간의 정보를 수집하는 과정을 의미

GC가 언제 실행될 지 알 수 없기 때문에 각 수집 사이클 전/후 메모리 상태를 알 수 없다. 따라서 GC 데이터를 깊이 있게 분석할 수 없다.

GC 로그의 장점

처음엔 GC 로깅을 JVM 구현체 디버깅 용도로 추가했다
거의 60개의 GC 플래그가 존재하는데, 이 플래그로 생성된 데이터 상당수가 성능 디버깅 목적으로 사용되었다.

➡️ 런타임에서 무슨 일이 발생했는지 정확히 파악하는데 GC 로깅이 아주 유용하다

GC 로깅은 핫스팟 JVM 내부에서 논블로킹 쓰기 매커니즘을 이용한다. Application 성능에 미치는 영향은 거의 0이므로 운영계에선 항상 켜두어야 한다

non-blocking?
특정 작업을 실행하는 동안 해당 작업이 완료될 때까지 다른 작업의 진행을 막지 않는 방식. 이는 다른 작업이 현재 진행 중인 작업의 완료를 기다리지 않고 동시에 진행될 수 있도록 함으로써, 전체 시스템의 처리량을 향상시키고 응답성을 높이는 데 도움을 준다

로그 파싱

GC 로그 메세지는 따로 표준 포맷이 없다.
출력되는 로그는 매우 복잡하다.
➡️ 따라서 GC 로그를 직접 파싱하려 하지말고, 반드시 툴을 사용하자!

센섬

  • jClarity사가 제작한 상용 메모리 분석기
  • 기능: GC 로그 파싱, 정보 추출, 자동 분석 기능

자동 분석 기능에서는 아래와 같은 기능을 제공한다

  • 정확한 할당률
  • 조기 승격
  • 확 치솟는 할당
  • 유저 이탈
  • 메모리 누수 감지
  • 힙 크기 조정 및 용량 계획
  • VM에 대한 OS 간섭
  • 크기를 잘못잡은 메모리 풀

GCViewer

  • 오픈소스로 무료이다
  • 상용 보다는 기능이 부실할 수 있다
    • 분석 기능은 없고, 특정 GC 핫스팟 로그 포맷만 파싱할 수 있다
  • https://github.com/chewiebug/GCViewer

GC 기본 튜닝

GC는 언제 튜닝해야 할까?
다음과 같은 사실을 기억하자!

  • GC가 성능 문제를 일으키는 근원이라고 확인하거나, 그렇지 않다고 배제하는 행위는 저렴하다
  • UAT(사용자 수용 테스트)에서 GC 플래그를 켜는 것도 저렴한 행위이다
  • 메모리 프로파일러 실행 프로파일러를 설저앟는 작업은 결코 저렴하지 않다

저렴하다..?
➡️ 적은 비용으로 큰 효과를 나타낸다?

튜닝에서 중요한 인자

  • 할당 (제일 중요 🌟)
  • 중단 민감도
  • 처리율 추이
  • 객체 수명

GC 힙 크기 조정 플래그

  • -Xms<size>: 힙 메모리의 최소 크기를 설정한다
  • -Xmx<size>: 힙 메모리의 최대 크기를 설정한다
  • -XX:MaxPermSize=<size>: 펌젠(Permgen) 메모리의 최대 크기를 설정한다(java 7 이전)
  • -XX:MaxMetaspaceSize=<size>: 메타스페이스 메모리의 최대 크기를 설정한다 (java 8 이후)

(참고) 펌젠과 메타스페이스

튜닝 시 주의할 것

  • 한번에 한 플래그씩 추가한다
  • 각 플래그가 무슨 작용을 하는지 숙지해야 한다
  • 부수 효과를 일으키는 플래그 조합도 있음을 명심한다

GC가 문제인가?

  1. CPU 사용률이 100%에 가까운가?
  2. 대부분의 시간(90% 이상)이 유저 공간에서 소비되는가?
  3. GC 로그가 쌓이고 있다면 현재 GC가 실행중이라는 증거

➡️ 3가지 조건이 다 맞다면 GC가 성능 이슈를 일으키고 있을 가능성이 크다!

문제 해결하기

GC가 문제라면 어떻게 해결해야 하는가?
➡️ 할당과 중단 시간 양상을 파악해야 한다

작가의 경험 상, 할당률의 수치가 1GB/s 이상으로 일정 시간 지속한다면 GC 튜닝만으로 해결할 수 없는 성능 문제이다
➡️ 이럴 경우 application 에서 해당 로직을 제거하는 리팩토링을 실행하여 메모리 효율을 개선해야 한다

GC 튜닝

Parallel GC 튜닝

Parallel GC는 가장 단순한 수집기라 튜닝 역시 제일 쉽다
최소한의 튜닝만으로 충분하다

Parallel GC 특성

  • 풀 STW
  • GC 처리율이 높고 계산 비용이 싸다
  • 부분 수집이 일어날 가능성이 없다

부분수집이란?
힙 메모리 전체를 대상으로 쓰레기를 수집하지 않고, 힙의 일부 영역만을 대상으로 가비지 컬렉션을 수행하는 과정

CMS 튜닝

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 튜닝

G1은 튜닝을 해도 어떤 영향을 미칠지 알아차리기가 어렵다
그래도 옵션을 지정해서 튜닝해야 할 경우
-XX:+UnlockExperimentalVMOptions 스위치를 지정하기!
-XX:G1NewSizePercent=<n>, -XX:G1MaxNewSizePercent=<n> 을 사용할 경우 꼭 필요하다

jHiccup

JVM이 연속적으로 실행되지 못한 지점(hiccup, 딸꾹질)을 보여주는 도구 이다

hiccup이 발생하는 이유
GC STW, OS나 플랫폼 관련 문제

profile
BackEnd Developer

0개의 댓글