Go 1.26부터 기본 적용된 Green Tea GC의 힙(heap) 상 메모리 할당 및 이동 방식을 분석함
비이동(non-moving) GC의 특성으로 인해 발생하는 페이지 재활용 문제를 지적함
Go의 크기 기반 할당(Size-based Allocation) 방식과 C# GC와의 성능 비교를 제시함
Go 1.26의 기본 GC인 Green Tea는 크기 기반 할당(Size-based Allocation) 전략을 사용합니다. 객체는 특정 크기 클래스(size class)로 반올림되어 스팬(span)이라 불리는 8KiB 페이지 청크에 할당됩니다. 제시된 Go 코드는 다양한 크기의 객체를 무작위로 할당하고, 힙을 워킹하며 객체의 위치와 크기를 시각화하여 이러한 연속적인 메모리 배치(Contiguous Memory Layout)를 명확히 보여줍니다. GC 실행 후에도 객체가 이동하지 않는 비이동(non-moving) 특성이 관찰됩니다.
커뮤니티에서는 비이동 GC의 근본적인 문제점으로 희소 페이지(Sparse Page) 재활용의 어려움을 지적합니다. Go GC는 객체를 이동시키지 않기 때문에, 할당된 객체들이 힙 전체에 흩어져 있을 경우 사용되지 않는 메모리 페이지를 효율적으로 회수하기 어렵습니다. 이는 메모리 단편화(Memory Fragmentation)를 야기하고, 결과적으로 캐시 효율성(Cache-friendliness) 저하 및 성능 병목 현상으로 이어질 수 있다는 분석입니다.
글에서는 Go의 Green Tea GC와 C#의 GC를 비교하며, 특히 메모리 할당 및 GC 동작 시점에서의 차이를 보여줍니다. Go의 예제 코드는 객체들이 크기별로 그룹화되어 할당되는 패턴을 시각적으로 드러내는 반면, C#의 GC는 일반적으로 압축(Compaction)을 통해 메모리 단편화를 줄이려는 시도를 합니다. 이러한 차이는 애플리케이션의 메모리 사용 패턴에 따라 GC 성능에 직접적인 영향을 미칠 수 있음을 시사합니다.
본문은 `perf`와 같은 성능 분석 도구(Performance Analysis Tools)를 활용하여 Go GC의 동작을 관찰하는 방법을 제시합니다. 이를 통해 개발자는 힙 할당(Heap Allocation), GC 주기(GC Cycle), 그리고 캐시 지역성(Cache Locality)과 같은 저수준(low-level) 메모리 관리 메커니즘을 깊이 이해할 수 있습니다. 특히, Go 런타임이 객체 크기에 따라 메모리를 어떻게 관리하는지에 대한 실질적인 통찰(Practical Insight)을 제공합니다.