CRuby GC의 개요 및 문제점
CRuby의 GC는 Mark & Sweep 알고리즘과 보수적 GC(Conservative GC)를 채택하고 있습니다. Ruby 객체는 RVALUE 구조체로 표현되며, 이 구조체 내의 flags에는 마크 비트와 타입 정보가 포함됩니다. GC는 루트(프로그램에서 직접 참조하는 객체)로부터 시작하여 살아있는 객체에 마크를 하고, 마크되지 않은 객체를 쓰레기로 간주하여 제거하는 방식으로 동작합니다.
그러나 기존 CRuby GC의 가장 큰 문제점은 ‘Stop The World’ 현상입니다. GC가 실행되는 동안 애플리케이션의 모든 동작이 멈추게 되는데, 객체 수가 증가할수록 최대 정지 시간도 비례하여 길어지는 알고리즘적 한계가 있었습니다. 이는 게임이나 로봇 제어와 같이 정지 시간이 중요한 애플리케이션에서 심각한 성능 저하를 유발했습니다.
지연 스윕(Lazy Sweep)의 도입
이러한 문제를 해결하기 위해 도입된 것이 ‘지연 스윕’입니다. 지연 스윕은 마크 단계가 완료된 후 스윕 작업을 즉시 수행하는 대신, 실제 객체 할당 요청이 발생할 때까지 스윕을 지연시키는 기술입니다. 이를 통해 스윕에 소요되는 부하를 객체 할당 시점으로 분산시켜 GC로 인한 최대 정지 시간을 줄이는 효과를 가져옵니다.
주요 구현 특징:
-
스윕 지연: 마크 완료 후 GC는 정지하며, 사용자 요청 시에만 블록 단위로 스윕을 수행하여 객체를 반환합니다.
-
힙 확장 타이밍 개선: 이전에는 스윕 시 미사용 객체 수를 세어 힙 확장 여부를 결정했으나, 지연 스윕에서는 마크 시점에 마크된 객체 수를 세어 사용 중인 객체 수를 파악하고, 이를 기반으로 힙 확장 및 스윕을 동시에 수행하여 부하를 분산합니다.
-
힙 구조 변경: 보수적 GC의 포인터 식별을 위한 힙 슬롯 정렬 문제를 해결하기 위해, 정렬된 힙 슬롯 배열이 실제 힙 블록을 가리키는 구조로 변경하여 지연 스윕의 관리를 용이하게 했습니다.
-
개발자 주의사항: 지연 스윕 환경에서는 마크가 객체에 계속 남아있으므로, 개발자가 임의로 마크를 제거할 경우 살아있는 객체가 해제되는 치명적인 버그를 유발할 수 있습니다.
성능 평가 및 데모
성능 평가 결과, 지연 스윕 도입 후 GC의 최대 정지 시간은 약 42% 개선되었습니다. 비록 전체 GC 소요 시간은 약 10% 증가했지만, 애플리케이션의 반응성을 저해하는 ‘최대 정지 시간’ 감소는 사용자 경험에 큰 영향을 미칩니다. ‘Kaboom’이라는 데모 애플리케이션을 통해 지연 스윕이 적용된 버전이 기존 버전보다 더 부드럽게 동작함을 시각적으로 확인할 수 있었습니다.
지연 스윕은 이미 CRuby 트렁크에 커밋되었으며, Ruby 1.9.3 버전에서 정식으로 사용 가능할 것으로 예상됩니다.