Card Table 개요
Card Table은 객체 참조 관계를 효율적으로 관리하기 위해 사용하는 데이터 구조이다. 주로 Generational GC에서 세대 간 참조를 추적하는 데 사용된다.
Card Table의 역할
- Generational GC의 효율성 보장
- Generational GC는 Young 세대와 Old 세대 간 객체 참조를 관리해야 한다. Card Table은 Old 세대에서 Young 세대를 참조하는 객체를 효율적으로 추적하기 위해 사용된다.
- Write Barrier와의 연계
- 객체 참조가 변경될 때마다 Write Barrier를 통해 Card Table에 해당 영역이 수정되었음을 기록한다. 이를 통해 GC는 Old 세대를 모두 스캔하지 않고, 수정된 영역만 스캔하도록 최적화된다.
- 최소화된 스캔 범위
- Card Table은 “Dirty Card”라는 상태를 통해 참조가 변경된 영역만 스캔하도록 돕는다. 이는 GC 작업량을 줄이는 데 기여한다.
Card Table의 구성 및 동작 원리
구성
- 힙 메모리는 여러 카드(Card)로 나뉜다. 각 카드의 크기는 고정된 단위(예: 512바이트, 1KB 등)이다.
- Card Table은 카드들의 상태를 나타내는 배열로 구성된다.
- 각 배열 엔트리는 해당 카드의 상태를 나타낸다. (예: 0은 참조 변경이 없음을, 1은 참조 변경이 있음을 나타낸다.)
Write Barrier
- 객체의 필드가 갱신될 때 Write Barrier가 호출된다.
- Write Barrier는 Card Table을 갱신하여 해당 카드가 Dirty 상태임을 기록한다.
GC 단계에서의 사용
- GC는 Card Table에서 Dirty 상태의 카드만 스캔한다.
- Dirty 상태의 카드 내에서 Young 세대로의 참조를 추적하고, 해당 객체들을 마킹한다.
- 이를 통해 Old 세대 전체를 스캔하지 않아도 된다.
Card Table의 장점
- 성능 최적화
- Old 세대 전체를 스캔하지 않아도 Dirty Card만 스캔하여 참조 관계를 확인한다. 이는 GC 성능을 크게 향상시킨다.
- 세대 간 참조 추적의 간소화
- Dirty Card 기반으로 참조를 탐지하므로, 세대 간 참조 관리가 단순화된다.
- 효율적인 메모리 사용
- Card Table은 힙 메모리 크기에 비해 매우 적은 메모리를 사용한다.
Card Table의 단점
- False Positives
- 한 카드 내의 일부 객체만 참조가 변경되더라도 카드 전체가 Dirty로 표시될 수 있다. 이는 불필요한 스캔을 초래한다.
- Write Barrier 오버헤드
- 객체 참조 변경 시 Write Barrier가 실행되므로 약간의 성능 오버헤드가 발생한다.
- 카드 크기 설정의 트레이드오프
- 카드 크기가 작으면 Card Table의 크기가 커지고 Write Barrier의 부하가 늘어난다. 카드 크기가 크면 False Positives가 증가한다.
결론
Card Table은 Generational GC에서 필수적인 데이터 구조로, 세대 간 참조를 효율적으로 관리하고 GC의 성능을 높이는 데 중요한 역할을 한다. 이를 통해 Young 세대의 객체를 빠르고 효율적으로 수집할 수 있으며, 메모리 사용량과 처리 시간을 최적화할 수 있다. 그러나 False Positives와 Write Barrier 오버헤드와 같은 단점도 존재하므로 시스템에 따라 적절히 조정해야 한다.