Kotlin & Java
JVM Garbage Collection Algorithm
상쾌한기분
2025. 4. 10. 13:59
반응형
JVM Garbage Collection Algorithm
Serial GC
- 가장 단순한 형태의 GC 알고리즘으로 단일 스레드로 동작.
- 단일 스레드에서 동작을하여 쓰레드간 통신 오버헤드가 발생하지 않아 경우에 따라서 상대적으로 효율적일 수도 있다.
- 싱글 코어에서 100MB 미만의 데이터를 다루는 경우 이점을 가질 수 있다.
- 특정 하드웨어나 OS 등의 환경에서는 default로 사용된다. (조선시대 환경)
java -XX:+UseSerialGC -jar Application.java
Parallel GC
- Java8 Default GC
- Throughput Collector 로도 불리며 Serial GC와 유사하다. 다른 점은 멀티 쓰레드 환경에서 GC를 수행한다는 것이다.
- 멀티 코어 환경에서 중간-큰 사이즈를 처리하는 어플리케션을 위해 설계
- Minor GC 와 Major GC 모두 멀티 쓰레드로 수행
병렬 압축(Parallel Compactiion) 특징으로 Major GC에서도 병렬로 수행할 수 있고 병렬 압축이 아니라면 싱글 쓰레드에서 동작한다. (UseParallelOldGC 옵션으로 제어할 수 있다.)
java -XX:+UseParallelGC -XX:ParallelGCThreads=<n> -jar Application.java
Garbage-First (G1) GC
- Java9 Default GC
- 멀티 코어 환경에 대용량 메모리 환경을 타겟으로 설계
- 힙 메모리 구조를 Region 개념을 사용
- 짧고 예측 가능한 GC Pause Time과 높은 처리량(Throughput)을 특징으로 다음 환경의 어플리케이션에서 최적화 되어 있다.
- 수십 GB 이상의 힙 사이즈 (50% 이상이 살아있는 데이터)
- 객체 할당과 승격(Promote)의 비율이 계속 다양하게 변경
- 신규 객체 생성과 참조 되어 살아남은 객체(Survivor, Old 영역으로 이동)의 비율
- 수백 ms 안으로 Pause Time이 예측 가능
- 힙 조각화(Fragmentation) 대응
- Region 기반 구조로 힙 조각화를 완하하고 메모리 압축 가능
- 다른 GC에 비교하여 짧은 중단 시간을 갖지만 처치량은 조금 낮은 경향이 있다.
- 가장 많은 Garbage가 존재하는 Region을 우선 수집(Grabage First)
java -XX:+UseG1GC -jar Application.java
Region 기반 힙 구조
전체 힙을 동일한 크기의 Region으로 나누며 (기본값: 1MB ~ 32MB) 각각의 Region은 동적으로 아래 중 하나가 될 수 있다.
- Young
- Eden: 빨간색
- Survivor: 빨간색 + S
- Old: 파랑색
- Humongous: 파란색 + H
- Region보다 큰 객체 전용
- Free
GC 동작 방식
G1 GC는 크게 두 단계로 반복 수행 된다.
- 젊은 세대 전용 단계(Young-Only phase)
- 젊은 세대에서 수집된 객체들이 늙은 세대로 이동되면서 늙은 세대의 사용량이 증가된다.
- 공간 회수 단계(Space-Relcamation phase)
- 젊은 세대와 늙은 세대에서 사용되지 않는 객체를 제거하고 메모리 공간을 확보한다.
Option and Default value | Description |
-XX:MaxGCPauseMillis=200 | 최대 중단 시간 |
-XX:GCPauseTimeInterval= | 최대 중단 시간의 간격, 극단적인 경우 연속적으로 GC가 수행될 수 있다. 기본값: 미설정 |
-XX:ParallelGCThreads= | 병렬 작업에 사용되는 최대 쓰레드 수. 기본값: 현재 CPU의 쓰레드 수가 8 이하라면 해당 값으로 설정, 8 초과라면 (초과 쓰레드 수) * 5 / 8 + 8 값으로 설정 |
-XX:ConcGCThreads= | 동시 작업에 사용되는 최대 쓰레드 수. 기본값: ParallelGCThreads / 4 |
-XX:+G1UseAdaptiveIHOP | 힙 점유율 제어. 기본값: 힙 점유율을 자동으로 결정하며, 초기 컬렉션 주기 동안 구세대 메모리의 45%를 마크 시작 임계값으로 사용 |
-XX:InitiatingHeapOccupancyPercent=45 | |
-XX:G1HeapRegionSize= | 힙 Region 크기. 기본값: 최대 힙 크기에 기준으로 계산되며 약 2048 Region을 생성되도록 계산된다. 반드시 2의 제곱 값이여야 하며 크기는 (1MB ~ 32MB) 이여야 한다. |
-XX:G1NewSizePercent=5 | 젊은 세대(young generation)의 전체 크기는 현재 사용 중인 Java 힙(heap) 메모리의 일정 비율로 결정되며, 이 값은 주어진 두 값 사이에서 변동 된다. |
-XX:G1MaxNewSizePercent=60 | |
-XX:G1HeapWastePercent=5 | 이 비율보다 컬렉션 후보 세트의 여유 공간이 적으면 공간 회수 단계(Space-Reclamation phase) 를 중단 |
-XX:G1MixedGCCountTarget=8 | 공간 회수 단계(Space-Reclamation phase)의 예상 소요 시간을 컬렉션(collection) 횟수로 표기한 값 |
-XX:G1MixedGCLiveThresholdPercent=85 | 이 비율보다 사용 중인 객체(live object) 비중이 높은 올드 세대(Old generation) 영역은 현재 공간 회수 단계(Space-Reclamation phase)에서 수집되지 않는다. |
1. 올드 세대(Old Generation) 처리 방식
- Parallel GC: 올드 세대 전체를 한 번에 압축(compact)하고 회수합니다. → 긴 정지 시간(pause time) 발생
- G1: 여러 번의 짧은 컬렉션으로 작업을 분산시켜 처리합니다. → 정지 시간 단축 (대신 처리량(throughput) 약간 감소 가능)
2. 동시성(Concurrency) 차이
- G1은 올드 세대 공간 회수(spacereclamation) 작업의 일부를 백그라운드에서 동시에 수행할 수 있습니다.
- 반면, Parallel GC 등은 STW(Stop-The-World) 방식으로만 동작합니다.
3. 오버헤드 및 처리량(Throughput)
- G1은 동시성 작업으로 인해 다른 수집기보다 오버헤드가 높아 처리량이 낮을 수 있습니다.
- ZGC (대용량 힙 전용)는 G1보다 더 짧은 정지 시간을 목표로 하지만, 처리량은 더욱 감소합니다.
G1의 고유한 최적화 기능
빈 대형 객체(Humongous Objects) 즉시 회수
- G1은 완전히 비어 있는 대형 올드 세대 영역을 즉시 회수할 수 있습니다.
→ 불필요한 GC 발생 감소 + 메모리 효율성 향상
(비활성화: -XX:-G1EagerReclaimHumongousObjects)
문자열 중복 제거(String Deduplication)
- Java 힙 내 중복 문자열을 동시에 검사해 제거하여 메모리 사용량을 줄입니다.
(기본 비활성화, 활성화: -XX:+G1EnableStringDeduplication)
The Z GC
ZGC는 확장성(Scalable) 있는 저지연(Low-Latency) GC로 애플리케이션을 최대 몇 ms의 중단 시간을 가지며 모든 고비용 작업을 동시(Concurrent)로 처리 한다.
- 초저지연으로 애플리케이션 중지 시간은 몇 ms 이내며 힙 크기와 무관하다. (대용량에서도 일관된 성능)
- 광범위한 힙 크기 지원으로 8MB ~ 16TB 까지 동작한다.
- 동시성(Concurrent) 중심 설계로 메모리 압축(Compatction), 마킹(Marking) 등 부한 큰 작업을 백그라운드에서 처리한다.
java -XX:+UseZGC -jar Application.java
힙 사이즈 (-Xmx)
ZGC의 가장 중요한 튜닝 옵션은 최대 힙 크기 지정이며 다음 두 조건을 만족하도록 해야한다.
- 애플리케이션의 사용 중인 객체 메모리 사용량(Live-set)를 수용할 수 있을 것
- GC가 실행 중일 때에도 메모리 할당이 가능하도록 충분한 여유 공간(Headroom)을 확보할 것
- 필요한 여유공간 크기는 애플리케이션의 할당 속도(Application Rate)와 사용 중인 객체 메모리 사용량 크기에 좌우된다.
- 일반적으로 ZGC 메모리를 많이 할당할수록 성능이 향상되지만, 불필요한 메모리 낭비 방지를 위해 적절한 균형을 찾아야 한다.
- 라이브 세트(Live-set): 현재 어플리케이션에서 실제로 사용 중인 객체들의 총 메모리 사용량
- 여유 공간(Headroom): 새 객체 할당을 위한 추가 메모리 버퍼
- 할당 속도(Allocation rate): 애플리케이션이 초당 객체를 생성하는 속도
동시성 GC 스레드 수 (-XX:ConcGCThreads)
두번째로 중요한 튜닝 옵션은 동시성 GC 스레드 수 지정이다. ZGC는 자동으로 이 값이 자동으로 선택되며 대부분 변경이 필요 하지 않지만 애플리케이션 특징에 따라 변경이 필요할 수 도 있다.
- 값을 너무 크게 설정하면 애플리케이션이 사용할 CPU가 부족
- 값을 너무 작게 설정하면 GC가 수집하는 속도보다 garbage가 더 빨리 쌓임.
메모리 반환(Uncommit) 동작
- 사용하지 않는 메모리는 OS로 자동 반환하며 메모리 사용량을 줄이는 환경에 유용하다.
- 비활성화 -XX:-ZUncommit
- 힙 크기는 최소 값 아래로 줄어들지 않는다.
- -Xms 설정
- 비활성 -Xms=1G -Xmx=1G 로 동일하게 설정시
- 메모리 반환 지연 시간 설정
- 기본값은 300초로 -XX:ZUncommitDelay=<second>
- 지정된 시간동안 미사용 상태라면 OS에 반환
출처
728x90
반응형