# Go의 Reaper를 속이기

> Clean Markdown view of GeekNews topic #20492. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=20492](https://news.hada.io/topic?id=20492)
- GeekNews Markdown: [https://news.hada.io/topic/20492.md](https://news.hada.io/topic/20492.md)
- Type: GN+
- Author: [xguru](https://news.hada.io/@xguru)
- Published: 2025-04-24T08:49:48+09:00
- Updated: 2025-04-24T08:49:48+09:00
- Original source: [mcyoung.xyz](https://mcyoung.xyz/2025/04/21/go-arenas/)
- Points: 1
- Comments: 1

## Topic Body

- Go 언어는 **정의되지 않은 동작**이 거의 없고, **간단한 GC(가비지 컬렉션) 의미론**을 가지고 있음  
- Go 에서는 **수동 메모리 관리**가 가능하며, 이는 GC와 협력하여 수행할 수 있음  
- **Arena**는 동일한 수명을 가진 메모리를 효율적으로 할당하는 데이터 구조로, Go에서 이를 구현하는 방법을 설명함  
- **Mark and Sweep** 알고리듬을 통해 GC가 메모리를 관리하는 방법을 설명함  
- Arena를 사용하여 **메모리 할당 성능**을 개선할 수 있으며, 이는 다양한 최적화를 통해 가능함  
- **Write barrier 제거, 메모리 재사용, chunk pooling** 등을 통해 성능 향상 및 GC 부담 최소화 시도  
- **Realloc 구현, Arena 재사용 및 초기화(Reset)** 기능 등을 통한 실제 대규모 메모리 처리 시의 안전하고 빠른 패턴 제시  
  
---  
  
### Go에서 Arena 기반 수동 메모리 할당의 개요  
  
- Go는 **명확한 GC 동작**과 **거의 없는 Undefined Behavior** 덕분에 안전한 언어임  
- `unsafe` 패키지를 활용해 **GC의 내부 구현에 맞춘 메모리 직접 제어**가 가능함  
- 이 글은 Go에서 GC와 협력 가능한 **Arena 구조 기반 메모리 할당기**를 만드는 방법을 설명함  
  
### Arena의 정의와 필요성  
  
- Arena는 **같은 수명의 객체를 효율적으로 할당**하기 위한 구조  
- 일반적인 `append`가 **지수적으로 배열을 확장**하는 방식이라면, Arena는 **새로운 블록을 추가하고 포인터를 제공**함  
- 표준 인터페이스는 다음과 같음:  
  - `Alloc(size, align uintptr) unsafe.Pointer`  
  
### 포인터와 GC의 작동 방식  
  
- GC는 **메모리를 추적(mark)하고 회수(sweep)**하는 방식으로 동작  
- **정확한 GC**를 위해 포인터 위치 정보를 알려주는 **pointer bits**라는 메타데이터 사용  
- Arena에서 포인터를 잘못 다루면, GC가 포인터를 추적하지 못해 **Use-After-Free 오류 발생 가능**  
  
### Arena 설계 방법  
  
- Arena 구조는 다음과 같은 필드를 가짐:  
  - `next`, `left`, `cap`, `chunks`  
- 모든 할당은 8바이트 정렬로 처리하고, 부족하면 `nextPow2`로 새로운 chunk 생성  
- chunk는 `[]uintptr` 대신 `struct { A [N]uintptr; P *Arena }` 타입으로 할당되어, **GC가 Arena를 추적 가능하게 함**  
  
### Arena의 포인터 안전성 확보 방법  
  
- Arena 내부에서만 할당된 포인터를 사용하는 경우 **GC가 Arena 전체를 유지시킴**  
- 포인터가 Arena를 참조하도록 설정해 **Arena 전체가 GC에서 생존 보장됨**  
- Arena의 할당 메서드는 다음을 수행함:  
  - `allocChunk()`에서 Arena 포인터를 chunk의 마지막에 저장  
  
### 성능 벤치마크 결과  
  
- 기본 `new` 대비 Arena 할당은 평균 **2~4배 이상의 성능 향상**을 보임  
- GC 부하가 큰 상황에서도 Arena 방식이 최대 **2배 이상 우수한 성능**을 보임  
- Write barrier 제거, `uintptr` 활용 등의 최적화는 **작은 할당에서 최대 20% 성능 향상**  
  
### Chunk 재사용 및 Heap 제거 전략  
  
- `sync.Pool`을 활용해 chunk 재사용 가능  
- `runtime.SetFinalizer()`를 통해 Arena가 사라질 때 chunk를 pool에 반환  
- 성능은 작은 할당에서 매우 좋아지지만, 큰 할당에서는 `new`보다 느려질 수 있음  
  
### Arena 초기화 및 재사용 기능  
  
- `Reset()` 메서드로 Arena를 초기 상태로 되돌릴 수 있음  
- 위험성이 크지만, 메모리 재할당 없이 동일 구조 재사용 가능  
- 재사용 시에도 chunk 재사용하여 성능 대폭 향상됨  
  
### Realloc 기능 구현  
  
- Arena에서 `realloc` 기능을 구현하여 가장 최근 할당에 대해 **동적 확장 가능**  
- 불가능한 경우 새로운 메모리를 할당 후 복사 처리  
  
### 결론 및 전체 코드 제공  
  
- Go의 GC 메커니즘을 깊이 이해하고, 내부 구현에 기반해 **Arena 기반 메모리 관리기**를 완성  
- 안전성과 성능을 모두 갖춘 구조이며, 적절히 사용하면 대규모 데이터 구조 처리에 매우 유용함  
- 전체 구현 코드는 Arena 구조체와 `New`, `Alloc`, `Reset`, `allocChunk`, `finalize` 등을 포함

## Comments



### Comment 37697

- Author: neo
- Created: 2025-04-24T08:49:49+09:00
- Points: 1

###### [Hacker News 의견](https://news.ycombinator.com/item?id=43756871) 
- 이 기사는 재미있는 읽을거리임
  - 이 기사를 즐겼거나 Go에서 메모리 할당을 더 잘 제어하고 싶다면, 내가 작성한 패키지를 확인해 보길 바람
  - 피드백을 받거나 다른 사람이 사용해 주면 좋겠음
  - 이 패키지는 런타임과 별도로 자체 메모리를 할당하여 GC를 완전히 우회함
  - 할당 시 포인터 타입을 허용하지 않지만, 동일한 기능을 제공하는 Reference[T] 타입으로 대체함
  - 메모리 해제는 수동으로 이루어지므로, 가비지 컬렉션에 의존할 수 없음
  - Go에서 이러한 커스텀 할당자는 일반적으로 함께 생성되고 소멸되는 할당 그룹을 지원하는 아레나를 지향함
  - 그러나 offheap 패키지는 대규모 장기 데이터 구조를 구축하여 가비지 컬렉션 비용을 제로로 만드는 것을 목표로 함
  - 대규모 인메모리 캐시나 데이터베이스 같은 것들임

- 최근 Go에서 성능 튜닝을 하면서 성능을 극대화하기 위해 매우 유사한 아레나 디자인을 사용하게 되었음
  - unsafe 포인터 대신 바이트 슬라이스를 buf와 청크로 사용함
  - 그렇게 해봤지만 더 빠르지 않고 훨씬 더 복잡했음
  - 100% 확신하기 전에 다시 확인해야 함

- 몇 가지 쉬운 개선점
  - 작은 슬라이스로 시작하고 일부 페이로드가 대량으로 추가되는 경우, 내장된 append를 호출하기 전에 cap을 더 공격적으로 증가시키는 자체 append를 작성함
  - unsafe.String은 바이트 슬라이스에서 문자열을 할당 없이 전달하는 데 유용함
  - 경고를 주의 깊게 읽고 무엇을 하는지 이해해야 함

- 주제와는 다르지만, 옆에 있는 미니맵이 마음에 듦
  - 긴 기술 기사에서 내용을 돌아다니며 읽거나 이전에 읽었던 내용을 다시 참조할 때 유용함
  - 내 사이트에 어떻게 추가할 수 있을지 궁금함
  - 정말 멋짐

- 긴 기사를 읽기 꺼리는 사람들을 위한 요약
  - OP는 Go에서 unsafe를 사용하여 할당자 작업을 가속화하기 위해 아레나 할당자를 구축함
  - 특히 함께 생성되고 소멸되는 많은 것을 할당할 때 유용함
  - 주요 문제는 Go의 GC가 데이터의 레이아웃(특히 포인터 위치)을 알아야 제대로 작동한다는 것임
  - unsafe.Pointer로 원시 바이트를 할당하면 GC가 아레나에서 가리키는 것을 제대로 볼 수 없어 실수로 해제할 수 있음
  - 그러나 포인터가 같은 아레나의 다른 것을 가리키는 한 작동하도록 하기 위해, 아레나의 일부가 여전히 참조되는 경우 전체 아레나를 유지함
  - (1) 시스템에서 아레나가 얻은 모든 큰 메모리 블록을 가리키는 슬라이스(청크)를 유지하고
  - (2) 이 블록에 추가 포인터 필드를 포함하는 새 유형을 생성하기 위해 reflect.StructOf를 사용함
  - 따라서 GC가 청크로의 포인터를 찾으면, 백 포인터도 찾게 되어 아레나를 살아있는 것으로 표시하고 청크 슬라이스를 유지함
  - 그런 다음 다양한 내부 검사와 쓰기 장벽을 제거하기 위한 흥미로운 최적화 기법을 소개함

- 관련: 표준 라이브러리에 "메모리 영역"을 추가하는 논의
  - 이전 아레나 제안

- 흥미로운 내용임
  - Go에서 오프힙 또는 아레나 스타일 할당자를 구축하는 사람들은 일반적으로 메모리 안전성과 GC 상호작용을 어떻게 테스트하거나 벤치마크하는지 궁금함

- Go는 생태계를 깨지 않도록 우선시함
  - 이는 Hyrum의 법칙이 런타임의 특정 관찰 가능한 동작을 보호할 것임을 가정할 수 있게 함
  - 이 주장이 맞다면, Go는 언어로서 진화의 막다른 길임
  - 이 경우 Go가 흥미로운지 확신할 수 없음

- 간단한 메타 노트
  - 이 기사는 정말 길어서 배경에 대한 세부 사항을 읽을 시간이 없음
  - 예를 들어 "Mark and Sweep" 섹션은 내 노트북 화면에서 4페이지 이상을 차지함
  - 그 섹션은 기사 시작 후 5페이지 이상 지나서 시작됨
  - AI가 섹션 작성에 도움을 주어 너무 포괄적이게 된 결과인지 궁금함
  - 콘텐츠 생성은 쉽지만 중요한 부분을 유지하기 위한 편집 결정이 이루어지지 않음
  - 아레나 할당자에 대한 부분만 알고 싶고 가비지 컬렉션에 대한 튜토리얼은 필요하지 않음
