[JVM] G1 Collector - Survivor 튜닝하기
1. 서바이버 스페이스 이해하기
Young Generation에서 객체들의 이동은 다음과 같다.
2. 서바이버 스페이스의 존재 이유
- 몇번의 Minor GC 동안 영 제너레이션 내에 객체가 남아있도록 설계하여, 올드 제너레이션으로 승격될 만큼 오래 살아남지 못한 객체들은 그 전에 제거한다.
- S0<->S1을 핑퐁을 쳐가며 오래 살아남지 못할 객체들은 Old Generation 전에 소거한다.
3. 서바이버 스페이스 오버플로우
서바이버 스페이스가 작아서 또는 어떤 이유로 꽉차게 되면 Minor GC발생 시, 객체가 에덴에서 올드 제너레이션으로 바로 승격된다. 오랫동안 사용되지 않을 객체라도 서바이버의 필터링을 거치지 못하기 때문에 올드 제너레이션으로 이동하게 되어 Full GC를 초래한다. 서바이버 스페이스가 꽉찬 상태를 서바이버 오버플로우라고 한다.
서바이버 오버플로우 발견하기 - Throughput Collector
- 뚜렷한 증거는 없다...
서바이버 오버플로우 발견하기 - CMS, G1 Collector
4. 서바이버 스페이스의 문제점
- 어짜피 오랫동안 사용될 객체라면, S0<->S1 핑퐁을 오래하면 오래할수록 서바이버 스페이스의 공간만 차지하여 서바이서 스페이스 오버플로우를 초래한다.
5. 서바이버 스페이스 튜닝하기
영 제너레이션에서 올드 제너레이션으로 객체들이 이동하는 경우는 두 가지가 있다.
1) Survivor Space Overflow - 서바이버 오버플로우
- Minor GC 가 발생했지만 S0 또는 S1이 꽉차면, 에덴에 남아있는 객체들인 올드 제너레이션으로 바로 이동한다
2) Tenuring threshold - 객체가 Survivor Space에 남아있는 횟수에 제한이 존재
튜닝 목적
위 두 가지 상황에 따른 튜닝 목적은 다음과 같다
1) 서바이버 오버플로우가 발생하지 않게하며,
2) Tenuring threshold 값을 잘.. 조정해서 오래 사용될 객체들만 목표 시간 내에! 올드 제너레이션으로 옮기자!
목표시간이라고 말한 이유는, 핑퐁을 몇번이상 거쳐서 살아남은 객체가 올드 객체라는 것은 정답이 없기 때문. 어플리케이션에 의존적이다!
단, 대부분의 어플리케이션에서 너무 많은 핑퐁은 좋지 않다.
어짜피 많이 사용될 객체는 몇번의 핑퐁을 거치든지 올드 제너레이션으로 갈 것이고, 이러한 객체들은 서바이버에 남아있을 수록 공간만 차지해서 서바이버 오버플로우를 발생시킨다.
튜닝 플래그
JVM은 아래 플래그들에 맞춰서 Survivor Space의 크기를 자동으로 조절한다.
JVM은 GC 후 서바이버 스페이스의 점유율을 확인하고, 플래그에 따라 크기를 자동으로 조정한다.
-XX:InitialSurvivorRatio = N
- 초기 서바이버 스페이스의 크기를 정한다.
- 아래 방정식에서 사용된다.
survivor_space_size = new_size(영 제너레이션) / ( InitialSurvivorRatio + 2)
InitialSurvivorRatio가 8이라면, 각 서바이버 스페이스 S0,S1은 영 제너레이션의 10%를 차지한다.
-XX:MinSurvivorRatio = N ( default:3)
- 플래그 이름이 조금..은 잘못되었다. 직관적이지 않다.
- 아래 방정식에서 사용된다.
maximum_survivor_space_size = new_size(영 제너레이션) / ( MinSurvivorRatio + 2)
- 디폴트값 3의 의미는, 서바이버 스페이스의 최대 크기는 영 제너레이션의 1/5, 20%를 차지한다는 의미이다.
서바이버 스페이스를 고정 크기로 하려면
SurvivorRatio를 원하는 값으로 설정하고, UseAdaptiveSizePolicy 플래그를 비활성화한다.
-XX:InitialTenuringThreshold=N (Default : 7, CMS : 6)
- Minor GC를 수행하면서 객체는 영 제너레이션에서 S0 <-> S1(핑퐁) 을 오가게 되는데, 이때 객체가 Survivor Space 에 남아있을 핑퐁의 한계치를 설정한다. TenuringThreshold를 조절해도, 너무 작은 값이 아닌 이상 사실상 몇번의 Minor GC 후에는 똑같이 올드 제너레이션으로 객체들이 이동하므로 큰 차이를 되지 않는다.
-XX:+AlwaysTenure ( default : false)
- Minor GC 에서 살아남은 객체들은 무조건 올드 제너레이션으로 승격시킨다
- InitialTenuringThreshold = 0과 같다
-XX:+NeverTenure ( default : false)
- Minor GC 에서 살아남은 객체들은 서바이버 스페이스가 있는 한, 절대로 올드제너레이션으로 승격되지 않는다.
- InitialTenuringThreshold = 무한대와 같다
6. 서바이버 스페이스 튜닝 Best Practice
- 힙의 전체적인 크기를 늘리고 영, 서바이버 스페이스를 같이 늘리는 것이 최고의 해결책
- 힙의 크기를 늘리고 서바이버의 비율을 줄이는 것을 추천. 서바이버의 크기가 작으면 적은 객체가 올드 제너레이션으로 승격되므로 Full GC 가 적어진다.
- 영 제너레이션의 크기만 늘리는 것은 좋지 않다. (이유는 생각해보기)
- 서바이버 스페이스의 크기가 튜닝되서, 오버플로우가 절대로 일어나지 않으면, 객체는 MaxTenuringThreshold 값에만 의존하여 올드 제너레이션으로 승격된다.
- MaxTenuringThreshold값은 서바이버 스페이스 내에 객체들을 오래 유지시키기 위해 늘릴 수 있다. 하지만 너무 커지면 서바이버 스페이스 내에 객체가 오래 머무는 만큼 공간이 부족하고 오버플로우가 발생하여 다시 에덴에서 올드제너레이션으로 직접 승격되는 오버플로우가 발생할 가능성이 높아진다.
==> 다시 말하지만, 몇번의 핑퐁을 거친 객체가 올드라는 것은 어플리케이션에 의존적이다!
'JAVA > JVM' 카테고리의 다른 글
[JVM] G1 Collector - 더! 큰 객체 할당 (0) | 2021.12.16 |
---|---|
[JVM] G1 Collector - 큰 객체 할당, TLAB 튜닝 (0) | 2021.12.16 |
[JVM] JMC, JFR - Memory Leak 진단하기 (0) | 2021.12.16 |
[JVM] JMC, JFR - 기본적인 사용법 (0) | 2021.12.16 |
[JVM] G1 Collector - 기본 튜닝 (0) | 2021.12.16 |
[JVM] G1 Collector - Full GC 발생하는 상황 (0) | 2021.12.16 |
[JVM] G1 Collector - GC Log (0) | 2021.12.16 |
[JVM] CMS Collector - PERM 튜닝 (0) | 2021.12.16 |