도입
이 블로그 게시물은 2025년 국제 메모리 관리 심포지엄에서 발표된 논문을 기반으로, Ruby 프로그래밍 언어의 메모리 관리 개선을 위한 MMTk(Memory Management Toolkit) 통합 프로젝트에 대해 설명합니다. 호주국립대학교(ANU)와 Shopify의 협력을 통해 진행되는 이 프로젝트는 Ruby의 현재 가비지 컬렉터(GC)인 Mark and Sweep을 대체할 차세대 고성능 GC를 구축하는 것을 목표로 합니다. MMTk는 모듈화된 VM-중립 프레임워크로, NoGC, Mark and Sweep, Immix, Generational Immix, Sticky Immix 등 다양한 고급 GC 알고리즘을 제공하여 상당한 성능 향상을 기대할 수 있습니다. 현재 Ruby용 MMTk 구현은 두 가지 형태로 존재하며, 본 글에서는 MMTk 팀의 선행 구현을 중심으로 다룹니다.
MMTk를 Ruby에 통합하는 과정에서 객체 이동(Copying GC)과 병렬 파이널라이제이션 단계에서 주요 기술적 도전 과제에 직면했습니다.
1. 객체 이동(Copying Garbage Collector) 처리
- 문제점: Ruby 2.7의 객체 이동 기능은 기존 데이터 타입의 객체 고정(pinning) 시스템과 MMTk 알고리즘의 마킹 및 이동 결합 방식이 충돌하여 비효율적인 힙 이중 스캔이 필요했습니다.
- 해결책:
- PPP(Potentially Pinning Parents) 개념 도입: 이동할 수 없는 참조를 포함할 가능성이 있는 객체를 PPP로 정의하고, 이들 객체를 최소화했습니다.
- 최적화된 스캔: MMTk는 살아있는 PPP 객체 목록을 활용하여 GC 주기 초기에 빠르게 고정될 자식 객체를 식별한 후 마킹 및 이동 단계를 진행함으로써 효율성을 높였습니다.
2. 병렬 파이널라이제이션 성능 저하
- 문제점: MMTk의 병렬 GC는 VM을 정지시키고 작동하는데, 초기에는 스레드 수가 증가할수록 파이널라이제이션 단계가 느려지는 현상이 발생했습니다. 이는
malloc으로 할당된 메모리를 해제하는 free 함수가 병렬 환경에서 스케일링되지 않아 병목 현상을 일으켰기 때문입니다.
- 해결책:
malloc 사용 회피: Array, String, MatchData 같은 일반 타입의 버퍼를 malloc 대신 MMTk가 직접 관리하는 숨겨진 Ruby 객체로 할당했습니다.
- 자동 메모리 관리: 이로써 버퍼 객체는 MMTk의 자동 메모리 관리를 받게 되었고, 파이널라이제이션 단계에서 별도의
free 작업이 필요 없어 성능 저하 문제를 해결했습니다.
결론
본 블로그 게시물에서는 Ruby에 MMTk를 통합하는 과정에서 마주했던 주요 도전 과제들, 즉 객체 이동을 위한 복사 가비지 컬렉터의 제약 사항과 병렬 파이널라이제이션 단계에서의 성능 저하 문제 및 그 해결책을 심도 있게 살펴보았습니다. PPP(Potentially Pinning Parents) 개념 도입과 `malloc` 사용을 회피하는 전략은 Ruby의 메모리 관리 효율성을 크게 개선하는 데 기여했습니다. 이러한 경험 공유는 Ruby 개발자, 가비지 컬렉터 연구자, 그리고 언어 설계자들에게 귀중한 통찰력을 제공할 것입니다. MMTk의 Ruby 포크에서는 최적화된 메모리 레이아웃, 새로운 객체 이동 기법, JIT 컴파일러와 GC의 통합 등 지속적인 연구 개발이 이루어지고 있으며, 이 과정에서 얻은 교훈은 Ruby 본류(upstream) 개선에도 활용될 예정입니다.