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