02 GC_알고리즘
02 GC_알고리즘
GC는 애플리케이션이 실행되는 도중에 루트(root)에서 객체까지의 참조 가능성(Reachability)을 안전하고 정확한 타이밍에 탐지하여 메모리를 해제하고, 힙영역을 최적화 하는 과정
1. Serial GC (단일 스레드 GC) - 초기 GC
1. 특징
- GC 중에는 모든 애플리케이션 스레드가 멈추고, 1개의 GC 스레드만 힙을 정리하며, GC가 끝나면 모든 스레드가 한꺼번에 다시 실행함.
- Serial GC는 단일 코어가 주를 이루던 시기에 개발 및 사용된 방식으로, 단일코어로 처리하게됨.
2. Serial GC 오버헤드
- 멀티코어 환경에서 Serial GC를 사용하는 것은 “CPU 입장에서는 Resource underutilization (자원 저활용)에 가깝고”, “사용자 입장에서는 CPU를 활용하지 못하니 STW가 길어지고 느려짐에 따라 오버헤드”, 멀티코어이기 때문에 GC자체가 안좋다기 보다, 자원은 많은데 활용하지 못해서 오버헤드가 발생했다 라고 표현하는것이 맞다.
3. Serial GC
- Serial GC를 최적으로 쓰기 위해서는 작은 시스템(Heap이 작을 수록, CPU 코어가 적을수록)에서 JVM을 운영할 경우에 유리함.
- C는 저수준으로 개발이 가능해서 하드웨어 자원이 제한적인 환경에서 유리한 편이지만, 자바도 일부 임베디드/작은 시스템용 경량 JVM이 존재하여 무조건 안 좋거나 불가능한 것은 아니다.
2. CMS GC (Concurrent Mark-Sweep GC)
1. 특징
- CMS는 Old Generation(Old gen)의 가비지 컬렉션 방식을 개선하기 위해 만들어진 GC 알고리즘
2. 사이클
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Example {
public static void main(String[] args) {
A objA = new A(); // 스택에 직접 참조되는 객체 A
objA.b = new B(); // A 객체가 참조하는 객체 B
}
}
class A {
B b;
}
class B {
}
- 마킹 비트 초기화
- 기존에 수행한 마크를 초기화
- Initial Mark ( STW 발생 )
- 루트(root)에서 직접 참조하는 객체들만 마킹을 진행함.(루트스캔)
- heap에서 전체 도달 가능한 객체에 마킹하는 것은 아님
- Concurrent Mark ( STW 해제 )
- 애플리케이션 실행과 병행하며 초기 마킹된 객체들의 도달 가능성을 탐색해서 도달 가능한 객체 마킹 확대
- remark ( STW 발생 )
- 변경된(추가/삭제/변경된) 참조 부분만 보완하는 역할
- 상대적으로 짧은 STW 발생, 이미 Initial의 루트스캔을 통해서 한번 검증되었기때문에,
- Concurrent Sweep ( STW 해제 )
- 도달 불가능한 객체들은 삭제
3. CMS GC - STW - CPU
- CMS는 Old 영역 도달성 조회를 루트 스캔과 힙 객체 탐색으로 분리하여, STW 시간을 최소화한 GC 방식
- Serial GC에서는 전체 조회를 하고 도달성을 조회하는 동안 STW가 발생했는데, CMS는 루트스캔과 루트로 부터 온 객체의 도달성조회를 분리해서 STW시간을 줄임
- 힙 객체 탐색과 애플리케이션 수행을 동시에 진행하기 때문에 CPU 사용률이 높아지는 단점이 있지만, 멀티코어 CPU 환경이 보편화되면서 이런 병행 처리 방식(CMS)이 현실적이고 효과적으로 사용할 수 있게 된 방식
4. CMS와 메모리 단편화
- CMS는 마킹-스윕(Mark-Sweep) 방식으로 작동하여 사용하지 않는 객체는 제거가 가능하지만, 메모리들을 연속적으로 정리하는 작업이 없음.
- 메모리가 충분해도 큰 객체 할당 실패하는 “OutOfMemoryError”가 발생할 수 있어서 Full GC(Stop-The-World + Serial 방식)사용이 불가피해지고, STW가 긴 시간이 필요해짐.
5. CMS GC
- CMS GC는 Serial GC에 비해 STW 시간을 크게 줄였지만, CPU를 더 많이 사용하게 되는 단점이 있음.
- 큰 힙에서는 병행 처리량이 많아지면서 CPU 부담이 커지고, 리소스 소모가 증가함
- 메모리 단편화(Fragmentation)가 발생할 가능성이 높아, 메모리 효율이 떨어지고, 결국 Full GC를 유발할 수 있는 리스크가 있음.
3. G1GC (Garbage First GC)
1. 특징
- 큰 힙과 멀티코어 환경에 최적화하기 위하여 만들어짐.
- 힙을 Region으로 나눔
- 힙 전체를 고정 크기 Region(리전) 단위로 분할하고, 각 Region은 Young, Old, Humongous 등 역할을 동적으로 할당함.
- 기존의 물리적으로 분리하던 방식 -> 개념적으로 분리
2. 사이클
단계 | CMS (Concurrent Mark Sweep) | G1GC (Garbage First GC) |
---|---|---|
Initial Mark | STW 발생, 루트(root)에서 Old 영역 직접 참조 객체만 마킹 | STW 발생, 루트에서 Eden + Survivor + 일부 Old Region 직접 참조 객체 마킹 |
Concurrent Mark | 애플리케이션 실행 중 Old 영역 전체 도달 가능 객체 마킹 | 애플리케이션 실행 중 힙 전체 Region 대상 도달 가능 객체 마킹 (Young + Old 모두 포함) |
Remark | STW 발생, 초기 마킹 중 변경된 참조 보완 | STW 발생, Concurrent Mark 중 변경된 참조 보완 (짧고 빠름) |
Concurrent Sweep | 애플리케이션 실행 중 Old 영역에서 가비지 객체 메모리 회수 | 애플리케이션 실행 중 가비지 많은 Region 우선적으로 복사/압축하여 메모리 회수 |
Compaction | 별도 단계 없이 메모리 단편화 발생 가능 | 복사/압축(Copy + Compaction) 수행해 단편화 방지 |
3. G1GC GC 영역 변화
- CMS에서는 Young영역의 GC방식은 변화가 없고, old영역에서만 변화를 주었지만, G1GC는 힙 전체를 region단위로 나눠서 GC하는 방법으로 변화
항목 | 기존 GC Young 영역 | G1GC Young 영역 |
---|---|---|
구조 | Eden + 2 Survivor 영역 고정 크기 | 여러 개 Region으로 구성 |
객체 이동 | Eden → Survivor → Survivor | Eden Region → Survivor Region(s) |
승격 방식 | 일정 나이 도달 시 Old 영역으로 이동 | Region 기반으로 누적 후 Old Region으로 이동 |
4. G1GC GC - pause time
- Region 단위로 GC 작업을 나누기 때문에, STW 시에 한 번에 처리하는 작업량을 조절할 수 있어, 개발자가 원하는 STW 시간을 설정할 수 있음
- STW 시간을 짧게 유지하려면 GC를 자주 실행해야 하므로, 결과적으로 CPU 사용량이 증가하는 트레이드오프(trade-off)가 발생함
5. G1GC GC
- CMS에 비해서는 메모리를 효율적으로 사용하고 처리하고, STW를 확실히 줄이는 것에 목표를 방식
- 목표 pause를 설정가능하기는 하지만 갑작스러운 변화나 복잡한 상황에서는 한계를 넘을 수가 있음.
- 힙 크기가 클수록 GC주기도 길고, CPU를 보다 많이 사용하게됨.
4. ZGC (Z Garbage Collector)
1. 특징
- 애플리케이션과 GC가 병행처리를 진행함.
- G1GC와 마찬가지로 ZGC도 region 기반 구조를 사용하지만, ZGC는 힙 전체를 단일 영역으로 간주하며, 객체의 크기와 위치를 추적하는 메타데이터와 색상 비트를 활용해 참조 상태를 관리
- 루트 스캔에 의한 STW가 제거되어, GC로 인한 정지가 거의 발생하지 않음.
2. 사이클
- Concurrent Mark (병행 마킹)
- 루트(root)에서 시작해 도달 가능한 객체를 마킹
- 애플리케이션이 실행되는 동안 동시에 수행됨
- Load Barrier로 객체 이동 및 참조 변화를 추적
- Prepare for Relocate (이동 준비)
- 객체 이동 계획 수립 및 메모리 공간 예약
- 병행 처리로 수행됨
- Concurrent Relocate (병행 이동)
- 살아있는 객체를 새 위치로 복사(이동)
- 애플리케이션 동작과 병행하여 진행
- 참조는 Load Barrier를 통해 자동 전환
- Concurrent Remap (병행 참조 재매핑)
- 이전 객체 주소를 새 주소로 참조 변경
- 애플리케이션이 참조하는 주소도 병행으로 안전하게 수정
- Stop-The-World Pause (짧은 정지)
- 메타데이터 정리 및 최종 점검
- 일반적으로 1~2ms 이하로 매우 짧음
3. ZGC - 루트스캔 - Load Barrier
- SerialGC, CMS GC, G1GC에서는 애플리케이션과 동시에 루트 스캔을 진행할 때, 참조값이 변경되거나 이동하면 안전성 보장이 어려웠음.
- ZGC는 Load Barrier (로드 바리어)를 통해서 객체를 참조할 때마다 JVM이 특수 코드를 삽입하여 이 부분을 해결함
- 애플리케이션에서 옛 참조(주소)를 사용할 경우 Load Barrier가 이를 가로채서 최신 참조로 변경함.
4. ZGC - Young gen/old gen - 색상비트
- 색상 비트(Color Bits)는 GC에서 객체의 상태를 표시하기 위해 사용하는 비트 플래그
- GC가 객체를 탐색하고 관리할 때, 객체가 “방문되었는지”, “마킹되었는지”, 혹은 “스캔 대상인지” 등의 상태 정보를 빠르게 판단함.
- 이를 통해서 세대를 관리하기 위해서 사용되던 리소르를 줄여서 오버헤드가 감소함.
5. ZGC
- 큰 힙 환경에 최적화 되어있음.
- 실시간으로 작동하는 Load Barrier 때문에 CPU점유율이 더 높아짐.
- 병행 처리 특성상 충분한 CPU코어 수가 필요해짐.
5. 용어
1. 오버헤드
- 직접적인 작업이 아닌 부가적인 처리로 인해 발생하는 시간, 비용, 자원 낭비
- 부가 작업 때문에 생기는 성능 비용
2. STW (Stop-The-World)
- JVM이 GC 또는 기타 내부 작업을 위해 모든 애플리케이션 쓰레드 실행을 일시적으로 중단시키는 현상
- GC가 진행하는 도중 애플리케이션이 동시에 메모리를 바꾸면 안 되기 때문에 모든 사용자 쓰레드를 멈춤.
- “STW가 발생한다”
3. 코어
- 싱글코어 -> 단일코어
- 다중코어 -> 멀티코어
4. 패턴
- 병렬 <-> 직렬
- 병행 <-> 순차적, 순서대로
5. RAM, CPU
하드웨어 요소 | GC와의 관계 | 영향 |
---|---|---|
RAM (메모리) | JVM 힙 크기 설정, 객체 저장 공간 | 힙 크기 → GC 빈도 및 정지 시간 영향 |
CPU | GC 스레드 실행, 병렬/병행 처리 가능 | CPU 성능 → GC 속도 및 애플리케이션 처리 영향 |
This post is licensed under CC BY 4.0 by the author.