Rust에서 unsafe 코드 없이 가비지 컬렉션(GC)을 구현하는 safe-gc 크레이트(crate)가 공개됨
기존 GC 라이브러리와 달리, safe-gc는 안전한 API(Safe API)를 제공하여 메모리 안전성을 보장함
safe-gc는 mark-and-sweep 방식을 사용하며, Root와 Gc 타입을 통해 메모리 접근을 제어함
저자는 복사 수집기(Copying Collector) 구현을 시도했으나, 복잡성 및 성능 문제로 인해 mark-and-sweep 방식을 선택함
safe-gc는 Rust의 소유권(Ownership) 및 빌림 규칙(Borrowing Discipline)을 준수하여 unsafe 코드를 사용하지 않고 가비지 컬렉션(GC)을 구현한다. 핵심은 Gc 포인터를 직접 dereference하는 대신, Heap을 통해 접근하도록 설계되었다는 점이다. 이를 통해, 컴파일 타임(Compile Time)에 메모리 안전성을 보장하며, use-after-free와 같은 메모리 관련 버그를 방지한다. 특히, Root와 Gc 타입을 사용하여 GC 객체에 대한 접근을 제어하는 방식이 주목할 만하다.
safe-gc는 mark-and-sweep 가비지 컬렉션(GC) 알고리즘을 사용한다. GC는 각 Arena의 mark bit를 초기화하고, Root로부터 시작하여 도달 가능한 모든 객체를 표시하는 방식으로 진행된다. Collector는 각 객체의 mark bit를 설정하고, mark stack을 관리한다. Arena는 각 객체에 대한 Trace 구현을 제공하여, GC가 올바르게 동작하도록 돕는다. 이러한 과정을 통해, safe-gc는 메모리 누수(Memory Leak) 없이 사용되지 않는 객체를 회수한다.
저자는 초기에는 복사 수집기(Copying Collector) 구현을 시도했으나, 이종 타입(Heterogeneous Types)을 지원하는 과정에서 어려움을 겪었다. Arena 내에서 forwarding pointer를 관리하는 과정에서, from-space에 대한 mutable access가 필요한 반면, 이미 Arena에 대한 접근이 이루어져 상호 배타적인 접근(Exclusive Access) 문제를 해결하지 못했다. 결과적으로, 복잡성 증가와 성능 저하를 우려하여 mark-and-sweep 방식을 선택했다. 이는 Rust의 소유권 규칙을 안전하게 지키면서 복잡한 GC 알고리즘을 구현하는 것이 얼마나 어려운지를 보여준다.
safe-gc는 Drop trait을 사용하여 안전하게 파이널라이저(Finalizer)를 구현한다. Drop 구현은 Heap에 직접 접근할 수 없도록 설계되어, use-after-free와 같은 파이널라이제이션(Finalization) 관련 버그를 방지한다. 이러한 설계는 safe-gc가 unsafe 코드를 사용하지 않으면서도 메모리 안전성을 확보하는 핵심적인 요소 중 하나이다. Drop trait을 통해, safe-gc는 객체가 더 이상 사용되지 않을 때 안전하게 리소스를 해제할 수 있도록 보장한다.
safe-gc는 dangling Gc를 사용할 경우 발생할 수 있는 문제점을 인지하고 있다. Trace trait 구현 오류나, GC가 발생하기 전에 unrooted Gc를 사용하는 경우, 객체가 수집될 수 있다. 이러한 경우, 접근 시 패닉(Panic)이 발생하거나, 잘못된 객체에 접근하는 ABA 문제가 발생할 수 있다. 하지만, safe-gc는 이러한 상황에서도 메모리 안전성을 보장하며, 런타임 오버헤드를 통해 문제를 완화한다. 세대 카운터(Generation Counter)를 추가하여, 이러한 문제를 더욱 효과적으로 해결할 수 있다.