[Loki] 자식 할당자가 반환한 메모리의 조기 해제 방지
PR 링크: grafana/loki#20508 상태: Merged | 변경: +61 / -27
들어가며
Loki의 메모리 할당자는 부모-자식 관계를 가질 수 있으며, 자식 할당자는 부모로부터 메모리를 빌려와 사용합니다. 기존에는 리전(region)의 상태가 "사용 중/해제됨"의 2가지뿐이었습니다. 자식 할당자가 부모의 리전을 임시로 사용한 후 반환하면 해당 리전이 "해제됨"으로 표시되는데, 이후 부모의 Reset(= Reclaim + Trim)에서 이 리전을 Go 런타임에 반환해버리는 문제가 있었습니다. 이는 아직 재사용할 수 있는 메모리를 불필요하게 해제하는 것입니다.
핵심 코드 분석
기존: 2상태 비트맵 (free)
Before:
type Allocator struct {
regions []*Region
free Bitmap // 1=free, 0=used
empty Bitmap
}
func (alloc *Allocator) Trim() {
for i := range alloc.free.IterValues(true) {
// free 비트맵이 true인 리전을 해제
// 자식이 반환한 리전도 여기서 해제됨 (문제!)
}
}
변경: 3상태 비트맵 (avail + used)
After:
type Allocator struct {
regions []*Region
avail Bitmap // 1=available, 0=in-use
used Bitmap // 1=이전 Reclaim 이후 사용됨, 0=미사용
empty Bitmap
}
func (alloc *Allocator) Trim() {
// used가 false인 리전만 해제 (이번 사이클에서 사용되지 않은 것만)
for i := range alloc.used.IterValues(false) {
region := alloc.regions[i]
if region == nil { continue }
if alloc.parent != nil {
alloc.parent.returnRegion(region)
}
// ...
}
}
자식 할당자가 부모에게 메모리를 반환할 때:
func (alloc *Allocator) returnRegion(region *Region) {
for i := range alloc.regions {
if alloc.regions[i] == region {
alloc.avail.Set(i, true) // avail만 설정, used는 건드리지 않음
break
}
}
}
왜 이게 좋은가
- 메모리 재사용 보장: 자식이 반환한 리전의
used비트가 유지되므로, 부모의 Trim에서 해제되지 않습니다. - GC 압력 감소: 불필요한 메모리 해제와 재할당 사이클이 줄어들어 Go 런타임의 GC 부담이 감소합니다.
- 명확한 상태 모델: 2상태(free/used)에서 3상태(avail/used/empty)로 전환하여 리전의 생명주기가 명확해집니다.
- 테스트 검증: 부모 Reset 후에도 자식이 반환한 리전이 동일한 포인터로 재할당되는지 검증하는 테스트가 추가되었습니다.
참고 자료
관련 포스트
PR Analysis 의 다른글
- 이전글 [Loki] 부모-자식 메모리 할당자 도입으로 계층적 메모리 수명 관리
- 현재글 : [Loki] 자식 할당자가 반환한 메모리의 조기 해제 방지
- 다음글 [triton] Triton 커널 최적화: High Occupancy Persistent Matmul 구현을 통한 성능 향상
댓글