본 발표는 RubyConf 2022에서 Peter Zhu가 진행한 'Ruby 가비지 컬렉터 최적화 및 튜닝'에 대한 강연 내용을 요약한 것입니다. 본 강연은 Ruby의 가비지 컬렉터(GC)가 어떻게 작동하는지, 애플리케이션 성능에 미치는 영향은 무엇인지, 그리고 GC 메트릭을 수집하고 해석하는 방법, 나아가 Rails 애플리케이션의 성능을 최적화하기 위한 다양한 튜닝 기법을 심도 있게 다룹니다. 특히 Ruby 3.3에 도입될 새로운 GC 기능들과 Shopify에서 개발한 `autotuner` 젬을 소개하며, 실제 프로덕션 환경에서의 GC 튜닝 효과와 실험 방법에 대한 실질적인 통찰을 제공합니다. Ruby on Rails 개발자에게 GC 최적화는 애플리케이션의 응답 시간을 개선하고 자원 효율성을 높이는 데 필수적인 요소임을 강조합니다.
Ruby의 가비지 컬렉터(GC)는 객체의 생명주기를 관리하며 메모리를 할당하고 회수합니다. Ruby는 GC 실행 시 모든 Ruby 코드 실행을 일시 정지시키는 ‘Stop-the-world’ 방식의 Mark & Sweep GC를 사용합니다. 객체는 마킹 과정을 통해 생존 여부가 결정되며, 이는 GC 소요 시간에 직접적인 영향을 미쳐 응답 시간 지연을 유발할 수 있습니다.
성능 최적화를 위해 Ruby는 ‘세대 가설’에 기반한 세대별 GC를 도입했습니다. 이는 객체를 Young Generation과 Old Generation으로 분류하며, Minor GC는 Young 객체만 처리하여 빠르지만, Major GC는 모든 객체를 처리하여 시간이 오래 걸리므로 최소화해야 합니다. Old 객체가 Young 객체를 참조할 때 발생하는 문제를 해결하기 위해 ‘Write Barriers’와 ‘Remember Set’이 사용됩니다. 또한, 메모리 단편화를 줄이는 ‘Compaction’ 단계도 존재합니다.
GC 성능 분석을 위해 GC.stat
과 Ruby 3.2부터 도입된 GC.stat_heap
을 활용할 수 있습니다. 이들 함수는 총 GC 주기, 소요 시간, 메모리 사용량, Major/Minor GC 빈도 등 상세한 메트릭을 제공하여 GC 병목 현상을 진단하는 데 도움을 줍니다.
주요 GC 튜닝 전략으로는 객체 할당량 자체를 줄이는 방법, 그리고 RUBY_GC_HEAP_INIT_SLOTS
및 RUBY_GC_OLDMALLOC_LIMIT_MAX
환경 변수를 통해 Major GC 주기를 조절하는 방법이 있습니다. Shopify는 후자를 통해 상당한 성능 개선을 이루었습니다. 또한, ‘Out-of-Band GC’는 요청 처리 사이 유휴 시간에 GC를 실행하여 요청 중 GC 발생을 방지하는 기법으로, forking 웹 서버에 효과적입니다.
Ruby 3.3에서는 GC 효율성을 높이는 두 가지 중요한 개선 사항이 도입됩니다. 첫째, RUBY_GC_REMEMBERED_WRITE_BARRIER_UNPROTECTED_OBJECTS_LIMIT_RATIO
환경 변수를 통해 Write Barrier Unprotected 객체 수에 따른 Major GC 트리거 임계치 계산 방식이 개선되어 불필요한 Major GC를 줄였습니다. 이로 인해 Shopify 앱의 GC 시간이 크게 감소했습니다. 둘째, Old 객체에서 Young 객체 참조 시 즉시 승격되던 기존 알고리즘이 변경되어, Young 객체가 일반적인 에이징 과정을 거치도록 개선되었습니다. 이 또한 GC 효율성 향상에 기여했습니다.
GC 튜닝 자동화를 위해 Shopify는 autotuner
젬을 개발했습니다. 이 젬은 GC 메트릭을 분석하여 최적의 튜닝 설정을 제안합니다. 튜닝 효과는 A/B 테스트와 유사한 실험 방식을 통해 검증하며, 소규모 ‘Experimental’ 그룹에 튜닝을 적용하고 ‘Untuned’ 그룹과 비교하여 성능 향상 여부를 판단한 후, 긍정적 효과 시 ‘Stable’ 그룹에 반영하는 방식으로 최적의 설정을 찾아 최종적으로 모든 서버에 롤아웃합니다.
결론적으로, Ruby on Rails 애플리케이션의 성능 최적화를 위해서는 Ruby 가비지 컬렉터의 내부 작동 방식을 이해하고, 관련 메트릭을 지속적으로 모니터링하며, 애플리케이션의 특성에 맞는 튜닝 전략을 적용하는 것이 필수적입니다. 객체 할당 최소화, Major GC 주기 조절, 그리고 Out-of-Band GC와 같은 기법은 응답 시간 개선에 크게 기여할 수 있습니다. 또한, Ruby 3.3에서 도입된 GC 개선 사항들과 `autotuner` 젬의 활용은 이러한 튜닝 과정을 보다 효율적이고 자동화된 방식으로 수행할 수 있도록 돕습니다. 지속적인 실험과 측정을 통해 각자의 Rails 애플리케이션에 최적화된 GC 설정을 찾아냄으로써, 전반적인 사용자 경험과 시스템 안정성을 향상시킬 수 있을 것입니다.