자바 최적화 책 정리
GC 로깅 개요
GC 로깅 켜기
✅ GC 스위치
-Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintTenuringDistribution
-XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps
Flag | Effect |
-Xloggc:gc.log | GC 이벤트에 로깅할 파일을 지정한다. |
-XX:+PrintGCDetails | GC 이벤트 세부 정보를 로깅한다. |
-XX:+PrintTenuringDistribution | 툴링에 꼭 필요한, 부가적인 GC 이벤트 세부 정보를 추가한다. |
-XX:+PrintGCTimeStamps | GC 이벤트 발생 시간을 (VM 시작 이후 경과한 시간을 초 단위로) 출력한다. |
-XX:+PrintGCDateStamps | GC 이벤트 발생 시간을 (벽시계 시간 기준으로) 출력한다. |
-> GC 로깅은 오버헤드가 거의 없고 훌륭한 원천 정보이기 때문에 자바 애플리케이션이라면 필수로 GC 로그를 보관해야 한다.
GC 로그 vs JMX
✅ JMX(Java Management eXtension)
자바 애플리케이션의 모니터링과 관리기능을 제공하는 기술.
-> 파일로 기록되는 GC 로그와 달리 실시간으로 모니터링할 수 있다.
JMX의 단점
✅ 가비지 수집
수집기가 언제 실행될지 예측할 수 없기 때문에, GC 관련 데이터를 정확하게 분석할 수 없다.
✅ RMI(Remote Method Invocation)
JMX는 RMI를 기반으로 애플리케이션과 통신하기 때문에, 추가 부하가 발생한다.
GC 로그 데이터의 장점
✅ 가비지 수집
GC 과정에서 무슨 일이 발생했는지 정확히 파악하는데 유용하다.
논블로킹 쓰기를 하기 때문에 성능에 미치는 영향이 거의 없다.
-> 성능 튜닝 활동에서 절대 빠질 수 없다.
로그 파싱 툴
✅ GC 로그 메시지는 표준 포맷이 없다
심지어 포맷도 조금씩 계속 바뀐다.
-> 스스로 GC 로그를 파싱하지 말고 반드시 툴을 사용하자.
센섬
✅ jClarity사가 제작한 상용 메모리 분석기
GC 로그 파싱, 정보 추출, 자동 분석 기능을 제공한다.
-> OpenJDK 로깅 관련 소스를 낱낱이 분석해서, 그 어느 툴보다도 다양한 GC 로그 설정을 지원한다.
GCViewer
✅ GC 로그 파싱 및 그래프 출력
분석 기능은 없고 GC 로그 파싱만 할 수 있다.
같은 데이터를 여러 가지 형태로 시각화하기
✅ 툴마다 시각화 형태가 다르다
<센섬>
<GCViewer>
GC 기본 튜닝
✅ 성능 문제의 원인인 GC인가?
- CPU 사용률이 100%에 가까운가?
- 대부분의 시간(90%)이 유저 공간에서 소비되는가?
- GC 로그가 쌓이가 있다면 현재 GC가 실행 중이라는 증거다.
-> 세 가지 조건이 다 맞는다면, GC가 성능 이슈를 일으키고 있을 가능성이 크다.
할당이란?
✅ 할당률을 줄여보자
1GB/s 이상으로 지속한다면 수집기 튜닝만으로는 해결할 수 없는 성능 문제가 터진 것이다.
-> 애플리케이션 할당 로직 리팩토링 필요.
✅ 덩치 큰 배열은 조기 승격될 가능성이 높다
객체가 너무 뚱뚱해서 에덴 영역에 안들어가면, 일찍 테뉴어드 영역으로 승격한다.
-XX:MaxTenuringThreshold=<n>
-> 테뉴어드 영역으로 승격되기 전까지 객체가 통과해야 할 가비지 수집 횟수
-> 한계치가 너무 낮으면 테뉴어드로 승격하는 객체가 많아지니 주의하자.
중단 시간이란?
✅ 중단 시간과 힙 크기에 대한 적절한 수집기
>1 s | 1 s–100 ms | <100 ms | < 2 GB |
Parallel | Parallel | CMS | < 4 GB |
Parallel | Parallel/G1 | CMS | < 4 GB |
Parallel | Parallel/G1 | CMS | < 10 GB |
Parallel/G1 | Parallel/G1 | CMS | < 20 GB |
Parallel/G1 | G1 | CMS | > 20 GB |
수집기 스레드와 GC 루트
✅ GC 루트 탐색 시간에 영향을 주는 요인
- 애플리케이션 스레드 개수: 스레드 별로 스택을 탐색해야 한다.
- 코드 캐시에 쌓인 컴파일드 코드량: JNI, JIT 컴파일드 코드에도 GC 루트가 있다.
- 힙 크기: 힙이 크면 탐색 시간이 길어진다.
✅ 카드 테이블 탐색
올드 영역으로 넘어온 객체의 루트를 추적할 때 탐색
-> 보통 20GB 힙에서 카드 테이블은 2MB 정도 필요하고 이를 탐색하는 시간은 대략 10ms 정도이다.
Parallel GC 튜닝
✅ 튜닝이 단순한 수집기
플래그 | 작용 |
-XX:NewRatio=<n> | (옛 플래그) 영 세대/전체 힙 비율 |
-XX:SurvivorRatio=<n> | (옛 플래그) 서바이버 공간/영 세대 비율 |
-XX:NewSize=<n> | (옛 플래그) 최소 영 세대 크기 |
-XX:MaxNewSize=<n> | (옛 플래그) 최대 영 세대 크기 |
-XX:MinHeapFreeRatio | (옛 플래그) 팽창을 막기 위한 GC 이후 최소 힙 여유 공간 비율(%) |
-XX:MaxHeapFreeRatio | (옛 플래그) 수축을 막기 위한 GC 이후 최대 힙 여유 공간 비율(%) |
-> 사람보다 프로그램이 크기를 알아서 잘 결정하기 때문에, 명시적으로 크기를 설정하는 일은 삼가는게 좋다.
CMS 튜닝
✅ 튜닝이 까다로운 수집기
CMS는 중단 시간을 줄이기 위한 여러 가지 복잡성을 가지고 있다.
-> CMS 플래그 값을 바꾸는 것에 대해 단순하게 생각하면 안된다.
그럼에도 불구하고 CMS 튜닝을 감행해야 한다면 다음 플래그를 참고하자.
-XX:ConcGCThreads=<n> // GC에 할당된 코어 수 조정 (애플리케이션 처리율을 늘리고 GC 속도를 낮춘다)
-XX:CMSInitiatingOccupancyFraction=<n> // CMS가 힙이 얼마나 찾을 때 수행할지 조정
-XX:+UseCMSInitiatingOccupancyOnly // 앞서 설정한 여유공간을 동적으로 조정하는 기능을 off한다
단편화로 인한 CMF
✅ 프리 리스트 통계 확인
CMS가 관리하는 프리 리스트로 인해 단편화가 발생하고, 나아가 CMF의 위험이 있다.
-XX:PrintFLSStatistics=1
-> 해당 스위치를 통해 GC 로그에 프리 리스트 관련 정보를 표시할 수 있다.
G1 튜닝
✅ G1도 CMS 처럼 옵션이 꽤 많다
튜닝이 어떤 영향을 미칠지 알아차리기가 어렵다.
-> 그래도 튜닝해야 할 경우 다음 스위치를 지정하자.
-XX:+UnlockExperimentalVMOptions
애플리케이션 할당률이 계속 높은 상태로 대부분 단명 객체가 생성되고 있다면 다음 튜닝을 고려해보자.
- 영 세대를 크게 설정한다.
- 테뉴어드 한계치를 최대 15 정도로 늘려 잡는다.
- 애플리케이션에서 수용 가능한 최장 중단 시간 목표를 정한다.
jHiccup
✅ JVM이 연속적으로 실행되지 못한 지점을 보여주는 계측 도구
ex) GC STW 중단
+) jHiccup은 HdrHistogram의 입력 데이터로 사용 가능한 결과를 출력한다.
jHiccupLogProcessor -i hiccup-example2.hlog -o alloc-example2
-> 위와 같은 간단한 명령으로 다음 그래프를 얻을 수 있다.
HdrHistogram과 jHiccup 세부 내용 참고
'book > 자바 최적화' 카테고리의 다른 글
[자바 최적화] JIT 컴파일의 세계로 (0) | 2023.09.20 |
---|---|
[자바 최적화] JVM의 코드 실행 (0) | 2023.09.11 |
[자바 최적화] 가비지 수집 고급 (0) | 2023.08.29 |
[자바 최적화] 가비지 수집 기초 (2) | 2023.08.28 |
[자바 최적화] 마이크로벤치마킹과 통계 (2) | 2023.08.23 |