[Go] clear
개요
Go 1.21부터 추가된 clear 내장 함수는 맵과 슬라이스의 모든 요소를 효율적으로 제거합니다.
주요 특징:
- 내장 함수: import 없이 사용 가능
- 맵 지원: 모든 키-값 쌍 제거
- 슬라이스 지원: 모든 요소를 제로 값으로 설정
- 용량 유지: 기존 할당된 메모리 유지
- 성능: 반복문보다 빠름
- 타입 안전: 컴파일 타임 타입 체크
- 간결성: 명확하고 읽기 쉬운 코드
기본 사용법
1. 맵에서 clear
package main
import "fmt"
func main() {
// 맵 생성 및 초기화
m := map[string]int{
"apple": 1,
"banana": 2,
"orange": 3,
}
fmt.Println("Before clear:", m)
fmt.Println("Length:", len(m))
// 맵의 모든 요소 제거
clear(m)
fmt.Println("After clear:", m)
fmt.Println("Length:", len(m))
// 맵 재사용 가능
m["grape"] = 4
fmt.Println("After adding:", m)
}
출력:
Before clear: map[apple:1 banana:2 orange:3]
Length: 3
After clear: map[]
Length: 0
After adding: map[grape:4]
2. 슬라이스에서 clear
func main() {
// 슬라이스 생성
slice := []int{1, 2, 3, 4, 5}
fmt.Println("Before clear:", slice)
fmt.Println("Length:", len(slice))
fmt.Println("Capacity:", cap(slice))
// 슬라이스의 모든 요소를 제로 값으로 설정
clear(slice)
fmt.Println("After clear:", slice)
fmt.Println("Length:", len(slice))
fmt.Println("Capacity:", cap(slice))
}
출력:
Before clear: [1 2 3 4 5]
Length: 5
Capacity: 5
After clear: [0 0 0 0 0]
Length: 5
Capacity: 5
3. 포인터 슬라이스에서 clear
type Person struct {
Name string
Age int
}
func main() {
// 포인터 슬라이스
people := []*Person{
{Name: "Alice", Age: 30},
{Name: "Bob", Age: 25},
{Name: "Carol", Age: 35},
}
fmt.Println("Before clear:")
for i, p := range people {
if p != nil {
fmt.Printf(" [%d] %+v\n", i, *p)
}
}
// 포인터를 nil로 설정 (메모리 누수 방지)
clear(people)
fmt.Println("After clear:")
for i, p := range people {
fmt.Printf(" [%d] %v\n", i, p)
}
}
출력:
Before clear:
[0] {Name:Alice Age:30}
[1] {Name:Bob Age:25}
[2] {Name:Carol Age:35}
After clear:
[0] <nil>
[1] <nil>
[2] <nil>
clear vs 다른 방법들
1. 맵 초기화 방법 비교
func main() {
// 방법 1: clear 사용 (권장)
m1 := map[string]int{"a": 1, "b": 2, "c": 3}
clear(m1)
fmt.Println("clear:", m1) // map[]
// 방법 2: 새 맵으로 재할당
m2 := map[string]int{"a": 1, "b": 2, "c": 3}
m2 = make(map[string]int)
fmt.Println("reassign:", m2) // map[]
// 방법 3: 반복문으로 삭제
m3 := map[string]int{"a": 1, "b": 2, "c": 3}
for k := range m3 {
delete(m3, k)
}
fmt.Println("loop delete:", m3) // map[]
}
비교:
clear: 가장 빠르고 명확, 메모리 재사용재할당: 새 메모리 할당, 이전 맵은 GC 대상반복문: 가장 느림, 각 키마다 delete 호출
2. 슬라이스 초기화 방법 비교
func main() {
// 방법 1: clear 사용
s1 := []int{1, 2, 3, 4, 5}
clear(s1)
fmt.Println("clear:", s1, "len:", len(s1), "cap:", cap(s1))
// clear: [0 0 0 0 0] len: 5 cap: 5
// 방법 2: 슬라이싱으로 길이 0
s2 := []int{1, 2, 3, 4, 5}
s2 = s2[:0]
fmt.Println("slice:", s2, "len:", len(s2), "cap:", cap(s2))
// slice: [] len: 0 cap: 5
// 방법 3: 새 슬라이스로 재할당
s3 := []int{1, 2, 3, 4, 5}
s3 = make([]int, 0, 5)
fmt.Println("reassign:", s3, "len:", len(s3), "cap:", cap(s3))
// reassign: [] len: 0 cap: 5
// 방법 4: 반복문으로 제로 값 설정
s4 := []int{1, 2, 3, 4, 5}
for i := range s4 {
s4[i] = 0
}
fmt.Println("loop:", s4, "len:", len(s4), "cap:", cap(s4))
// loop: [0 0 0 0 0] len: 5 cap: 5
}
차이점:
clear(s): 요소를 제로 값으로, 길이 유지s[:0]: 길이를 0으로, 용량 유지, 요소는 그대로make: 새 메모리 할당반복문: clear와 동일하지만 느림
성능 비교
1. 맵 벤치마크
package main
import (
"testing"
)
const mapSize = 10000
func BenchmarkMapClear(b *testing.B) {
m := make(map[int]int, mapSize)
for i := 0; i < mapSize; i++ {
m[i] = i
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
clear(m)
// 다시 채우기
for j := 0; j < mapSize; j++ {
m[j] = j
}
}
}
func BenchmarkMapReassign(b *testing.B) {
m := make(map[int]int, mapSize)
for i := 0; i < mapSize; i++ {
m[i] = i
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
m = make(map[int]int, mapSize)
// 다시 채우기
for j := 0; j < mapSize; j++ {
m[j] = j
}
}
}
func BenchmarkMapDelete(b *testing.B) {
m := make(map[int]int, mapSize)
for i := 0; i < mapSize; i++ {
m[i] = i
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for k := range m {
delete(m, k)
}
// 다시 채우기
for j := 0; j < mapSize; j++ {
m[j] = j
}
}
}
예상 결과:
BenchmarkMapClear 5000 250000 ns/op
BenchmarkMapReassign 3000 450000 ns/op
BenchmarkMapDelete 2000 850000 ns/op
clear가 가장 빠름!
2. 슬라이스 벤치마크
const sliceSize = 10000
func BenchmarkSliceClear(b *testing.B) {
s := make([]int, sliceSize)
b.ResetTimer()
for i := 0; i < b.N; i++ {
clear(s)
}
}
func BenchmarkSliceLoop(b *testing.B) {
s := make([]int, sliceSize)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for j := range s {
s[j] = 0
}
}
}
func BenchmarkSliceReslice(b *testing.B) {
s := make([]int, sliceSize)
b.ResetTimer()
for i := 0; i < b.N; i++ {
s = s[:0]
s = s[:cap(s)]
}
}
예상 결과:
BenchmarkSliceClear 50000 25000 ns/op
BenchmarkSliceLoop 30000 40000 ns/op
BenchmarkSliceReslice 100000 10000 ns/op (단, 요소는 그대로)
3. 메모리 할당 비교
func main() {
var m runtime.MemStats
// clear 사용
runtime.ReadMemStats(&m)
before := m.Alloc
myMap := make(map[int]int)
for i := 0; i < 100000; i++ {
myMap[i] = i
}
clear(myMap)
runtime.ReadMemStats(&m)
after := m.Alloc
fmt.Printf("clear: %d bytes\n", after-before)
// 재할당 사용
runtime.ReadMemStats(&m)
before = m.Alloc
myMap = make(map[int]int)
for i := 0; i < 100000; i++ {
myMap[i] = i
}
myMap = make(map[int]int)
runtime.ReadMemStats(&m)
after = m.Alloc
fmt.Printf("reassign: %d bytes\n", after-before)
}
clear는 메모리를 재할당하지 않음!
메모리 동작
1. 맵의 메모리 동작
func main() {
// 큰 맵 생성
m := make(map[int]int)
for i := 0; i < 1000000; i++ {
m[i] = i
}
fmt.Println("Before clear, length:", len(m))
// clear 후에도 내부 버킷은 유지됨
clear(m)
fmt.Println("After clear, length:", len(m))
// 메모리는 여전히 할당되어 있어 빠르게 재사용 가능
for i := 0; i < 1000000; i++ {
m[i] = i
}
fmt.Println("After refill, length:", len(m))
}
2. 슬라이스의 메모리 동작
func main() {
// 슬라이스 생성
s := make([]int, 1000)
for i := range s {
s[i] = i
}
fmt.Printf("Before: len=%d, cap=%d, first=%v\n",
len(s), cap(s), s[0])
// clear는 용량과 길이를 유지하고 요소만 제로 값으로
clear(s)
fmt.Printf("After: len=%d, cap=%d, first=%v\n",
len(s), cap(s), s[0])
// 메모리 재사용 (재할당 없음)
for i := range s {
s[i] = i * 2
}
fmt.Printf("Refill: len=%d, cap=%d, first=%v\n",
len(s), cap(s), s[0])
}
출력:
Before: len=1000, cap=1000, first=0
After: len=1000, cap=1000, first=0
Refill: len=1000, cap=1000, first=0
3. 포인터 슬라이스와 GC
type LargeStruct struct {
Data [1000]int
}
func main() {
// 포인터 슬라이스
s := make([]*LargeStruct, 1000)
for i := range s {
s[i] = &LargeStruct{}
}
fmt.Println("Created 1000 large structs")
// clear 없이 슬라이싱만 하면 메모리 누수
// s = s[:0] // ❌ 포인터는 여전히 참조됨
// clear로 포인터를 nil로 설정 (GC 가능)
clear(s) // ✅ 포인터가 nil이 되어 GC 가능
s = s[:0]
runtime.GC()
fmt.Println("Memory can be reclaimed")
}
실전 예제
1. 캐시 초기화
type Cache struct {
data map[string]interface{}
mu sync.RWMutex
}
func NewCache() *Cache {
return &Cache{
data: make(map[string]interface{}),
}
}
func (c *Cache) Set(key string, value interface{}) {
c.mu.Lock()
defer c.mu.Unlock()
c.data[key] = value
}
func (c *Cache) Get(key string) (interface{}, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
value, ok := c.data[key]
return value, ok
}
func (c *Cache) Clear() {
c.mu.Lock()
defer c.mu.Unlock()
clear(c.data) // 효율적인 초기화
}
func (c *Cache) Size() int {
c.mu.RLock()
defer c.mu.RUnlock()
return len(c.data)
}
func main() {
cache := NewCache()
cache.Set("key1", "value1")
cache.Set("key2", "value2")
cache.Set("key3", "value3")
fmt.Println("Size before clear:", cache.Size()) // 3
cache.Clear()
fmt.Println("Size after clear:", cache.Size()) // 0
}
2. 객체 풀 재사용
import "sync"
type Buffer struct {
data []byte
}
var bufferPool = sync.Pool{
New: func() interface{} {
return &Buffer{
data: make([]byte, 0, 4096),
}
},
}
func GetBuffer() *Buffer {
return bufferPool.Get().(*Buffer)
}
func PutBuffer(buf *Buffer) {
// 버퍼 내용 제거 (메모리는 유지)
clear(buf.data)
buf.data = buf.data[:0]
bufferPool.Put(buf)
}
func main() {
// 버퍼 가져오기
buf := GetBuffer()
// 사용
buf.data = append(buf.data, []byte("Hello, World!")...)
fmt.Println("Buffer:", string(buf.data))
// 풀에 반환 (재사용)
PutBuffer(buf)
// 다시 가져오기 (같은 메모리 재사용 가능)
buf2 := GetBuffer()
fmt.Println("Reused buffer length:", len(buf2.data)) // 0
fmt.Println("Reused buffer capacity:", cap(buf2.data)) // 4096
}
3. 배치 처리
type Processor struct {
batch []int
size int
}
func NewProcessor(batchSize int) *Processor {
return &Processor{
batch: make([]int, 0, batchSize),
size: batchSize,
}
}
func (p *Processor) Add(value int) {
p.batch = append(p.batch, value)
if len(p.batch) >= p.size {
p.Process()
}
}
func (p *Processor) Process() {
if len(p.batch) == 0 {
return
}
fmt.Printf("Processing batch of %d items: %v\n", len(p.batch), p.batch)
// 배치 처리 후 초기화 (메모리 재사용)
clear(p.batch)
p.batch = p.batch[:0]
}
func (p *Processor) Flush() {
p.Process()
}
func main() {
processor := NewProcessor(5)
for i := 1; i <= 12; i++ {
processor.Add(i)
}
// 남은 항목 처리
processor.Flush()
}
출력:
Processing batch of 5 items: [1 2 3 4 5]
Processing batch of 5 items: [6 7 8 9 10]
Processing batch of 2 items: [11 12]
4. 맵 재사용 패턴
type RequestHandler struct {
headers map[string]string
}
func NewRequestHandler() *RequestHandler {
return &RequestHandler{
headers: make(map[string]string, 10), // 미리 할당
}
}
func (h *RequestHandler) HandleRequest(req map[string]string) {
// 이전 요청의 헤더 제거
clear(h.headers)
// 새 헤더 설정
for k, v := range req {
h.headers[k] = v
}
// 처리
fmt.Println("Processing request with headers:", h.headers)
}
func main() {
handler := NewRequestHandler()
// 첫 번째 요청
handler.HandleRequest(map[string]string{
"Content-Type": "application/json",
"Accept": "application/json",
})
// 두 번째 요청 (맵 재사용)
handler.HandleRequest(map[string]string{
"Content-Type": "text/html",
"User-Agent": "MyApp/1.0",
})
}
5. 슬라이스 워커 풀
type WorkerPool struct {
workers []*Worker
}
type Worker struct {
ID int
Tasks []Task
}
type Task struct {
Name string
}
func NewWorkerPool(numWorkers int) *WorkerPool {
workers := make([]*Worker, numWorkers)
for i := range workers {
workers[i] = &Worker{
ID: i,
Tasks: make([]Task, 0, 100),
}
}
return &WorkerPool{workers: workers}
}
func (wp *WorkerPool) AssignTask(workerID int, task Task) {
if workerID >= 0 && workerID < len(wp.workers) {
wp.workers[workerID].Tasks = append(wp.workers[workerID].Tasks, task)
}
}
func (wp *WorkerPool) ProcessAll() {
for _, worker := range wp.workers {
worker.Process()
}
}
func (w *Worker) Process() {
if len(w.Tasks) == 0 {
return
}
fmt.Printf("Worker %d processing %d tasks\n", w.ID, len(w.Tasks))
// 작업 처리 후 초기화
clear(w.Tasks)
w.Tasks = w.Tasks[:0]
}
func main() {
pool := NewWorkerPool(3)
pool.AssignTask(0, Task{Name: "Task1"})
pool.AssignTask(0, Task{Name: "Task2"})
pool.AssignTask(1, Task{Name: "Task3"})
pool.AssignTask(2, Task{Name: "Task4"})
pool.ProcessAll()
}
6. 통계 수집기
type StatsCollector struct {
samples []float64
results map[string]float64
}
func NewStatsCollector() *StatsCollector {
return &StatsCollector{
samples: make([]float64, 0, 1000),
results: make(map[string]float64),
}
}
func (sc *StatsCollector) AddSample(value float64) {
sc.samples = append(sc.samples, value)
}
func (sc *StatsCollector) Calculate() map[string]float64 {
if len(sc.samples) == 0 {
return sc.results
}
// 이전 결과 제거
clear(sc.results)
// 평균
var sum float64
for _, v := range sc.samples {
sum += v
}
sc.results["mean"] = sum / float64(len(sc.samples))
// 최소/최대
min, max := sc.samples[0], sc.samples[0]
for _, v := range sc.samples {
if v < min {
min = v
}
if v > max {
max = v
}
}
sc.results["min"] = min
sc.results["max"] = max
return sc.results
}
func (sc *StatsCollector) Reset() {
clear(sc.samples)
sc.samples = sc.samples[:0]
clear(sc.results)
}
func main() {
collector := NewStatsCollector()
// 첫 번째 데이터 세트
for _, v := range []float64{1.5, 2.3, 3.7, 4.2, 5.1} {
collector.AddSample(v)
}
stats1 := collector.Calculate()
fmt.Println("Stats 1:", stats1)
// 리셋
collector.Reset()
// 두 번째 데이터 세트
for _, v := range []float64{10.0, 20.0, 30.0} {
collector.AddSample(v)
}
stats2 := collector.Calculate()
fmt.Println("Stats 2:", stats2)
}
특수 케이스
1. nil 맵과 슬라이스
func main() {
var m map[string]int
var s []int
// nil 맵이나 슬라이스에 clear 호출 시 패닉
// clear(m) // panic: runtime error
// clear(s) // panic: runtime error
// 안전한 사용
if m != nil {
clear(m)
}
if s != nil {
clear(s)
}
// 또는 make로 초기화
m = make(map[string]int)
s = make([]int, 10)
clear(m) // OK
clear(s) // OK
}
2. 빈 맵과 슬라이스
func main() {
// 빈 맵
m := make(map[string]int)
clear(m) // OK, 아무 일도 일어나지 않음
// 빈 슬라이스
s := make([]int, 0)
clear(s) // OK, 아무 일도 일어나지 않음
// 길이 0인 슬라이스
s2 := []int{1, 2, 3}
s2 = s2[:0]
clear(s2) // OK, 아무 일도 일어나지 않음 (길이가 0)
}
3. 중첩된 구조
func main() {
// 맵의 맵
m := map[string]map[string]int{
"group1": {"a": 1, "b": 2},
"group2": {"c": 3, "d": 4},
}
// 외부 맵만 clear (내부 맵은 그대로)
clear(m)
fmt.Println("Outer map:", m) // map[]
// 중첩된 맵도 clear하려면
m = map[string]map[string]int{
"group1": {"a": 1, "b": 2},
"group2": {"c": 3, "d": 4},
}
for _, inner := range m {
clear(inner)
}
clear(m)
// 슬라이스의 슬라이스
s := [][]int{
{1, 2, 3},
{4, 5, 6},
}
// 외부 슬라이스만 clear
clear(s) // [[0 0 0] [0 0 0]]로 변경 (포인터가 nil이 됨)
}
4. 구조체 슬라이스
type Person struct {
Name string
Age int
}
func main() {
people := []Person{
{Name: "Alice", Age: 30},
{Name: "Bob", Age: 25},
}
fmt.Println("Before:", people)
// 각 요소가 제로 값으로
clear(people)
fmt.Println("After:", people)
// After: [{Name: Age:0} {Name: Age:0}]
}
일반적인 실수
1. nil 체크 누락
// ❌ 나쁜 예
func clearMap(m map[string]int) {
clear(m) // m이 nil이면 패닉!
}
// ✅ 좋은 예
func clearMap(m map[string]int) {
if m != nil {
clear(m)
}
}
// ✅ 또는 새 맵 반환
func clearMap(m map[string]int) map[string]int {
if m == nil {
return make(map[string]int)
}
clear(m)
return m
}
2. 슬라이스 길이 변경 기대
// ❌ 잘못된 기대
func main() {
s := []int{1, 2, 3, 4, 5}
clear(s)
// clear는 길이를 변경하지 않음!
fmt.Println(len(s)) // 여전히 5
// 길이를 0으로 만들려면
s = s[:0]
}
// ✅ 올바른 사용
func main() {
s := []int{1, 2, 3, 4, 5}
clear(s) // 요소를 제로 값으로
s = s[:0] // 길이를 0으로
}
3. 포인터 슬라이스 메모리 누수
// ❌ 나쁜 예 (메모리 누수)
func main() {
s := make([]*LargeObject, 1000)
for i := range s {
s[i] = &LargeObject{} // 큰 객체 할당
}
s = s[:0] // 길이만 0, 포인터는 여전히 참조됨!
}
// ✅ 좋은 예
func main() {
s := make([]*LargeObject, 1000)
for i := range s {
s[i] = &LargeObject{}
}
clear(s) // 포인터를 nil로 (GC 가능)
s = s[:0]
}
4. 맵 재할당과 혼동
// ❌ 불필요한 재할당
func resetMap(m map[string]int) map[string]int {
return make(map[string]int) // 새 메모리 할당
}
// ✅ clear 사용
func resetMap(m map[string]int) {
clear(m) // 기존 메모리 재사용
}
5. 동시성 안전성 가정
// ❌ 나쁜 예 (경쟁 상태)
type Cache struct {
data map[string]int
}
func (c *Cache) Clear() {
clear(c.data) // 동시 접근 시 위험!
}
// ✅ 좋은 예
type Cache struct {
data map[string]int
mu sync.RWMutex
}
func (c *Cache) Clear() {
c.mu.Lock()
defer c.mu.Unlock()
clear(c.data)
}
6. 반환 후 clear
// ❌ 나쁜 예
func getAndClear() []int {
s := []int{1, 2, 3}
clear(s)
return s // [0 0 0] 반환
}
// ✅ 의도에 맞게
func getAndClear() []int {
s := []int{1, 2, 3}
result := make([]int, len(s))
copy(result, s)
clear(s)
return result // [1 2 3] 반환
}
7. 중첩 구조 부분 clear
// ❌ 불완전한 clear
func main() {
m := map[string][]int{
"a": {1, 2, 3},
"b": {4, 5, 6},
}
clear(m) // 외부 맵만 clear, 내부 슬라이스는?
}
// ✅ 완전한 clear
func main() {
m := map[string][]int{
"a": {1, 2, 3},
"b": {4, 5, 6},
}
// 내부 슬라이스도 clear
for k := range m {
clear(m[k])
}
clear(m)
}
베스트 프랙티스
1. nil 체크 함수
func ClearMap[K comparable, V any](m map[K]V) {
if m != nil {
clear(m)
}
}
func ClearSlice[T any](s []T) {
if s != nil {
clear(s)
}
}
// 사용
var myMap map[string]int
ClearMap(myMap) // 안전함
2. 메서드로 캡슐화
type Container struct {
items []int
}
func (c *Container) Clear() {
if c.items != nil {
clear(c.items)
c.items = c.items[:0]
}
}
func (c *Container) Reset() {
if c.items != nil {
clear(c.items)
c.items = c.items[:0:cap(c.items)]
}
}
3. 재사용 패턴
type Reusable struct {
buffer []byte
cache map[string]interface{}
}
func NewReusable() *Reusable {
return &Reusable{
buffer: make([]byte, 0, 4096),
cache: make(map[string]interface{}, 100),
}
}
func (r *Reusable) Reset() {
if r.buffer != nil {
clear(r.buffer)
r.buffer = r.buffer[:0]
}
if r.cache != nil {
clear(r.cache)
}
}
func (r *Reusable) Use() {
// 사용
r.buffer = append(r.buffer, []byte("data")...)
r.cache["key"] = "value"
}
4. 풀과 함께 사용
var itemPool = sync.Pool{
New: func() interface{} {
return &Item{
data: make([]byte, 0, 1024),
}
},
}
type Item struct {
data []byte
}
func (i *Item) Reset() {
clear(i.data)
i.data = i.data[:0]
}
func GetItem() *Item {
return itemPool.Get().(*Item)
}
func PutItem(item *Item) {
item.Reset()
itemPool.Put(item)
}
5. 문서화
// ClearCache removes all entries from the cache while preserving
// the allocated memory for reuse. This is more efficient than
// creating a new map.
//
// This method is safe to call on a nil cache.
func (c *Cache) ClearCache() {
if c != nil && c.data != nil {
clear(c.data)
}
}
6. 테스트에서 활용
func TestHandler(t *testing.T) {
handler := NewHandler()
testCases := []struct {
name string
input string
want string
}{
{"case1", "input1", "output1"},
{"case2", "input2", "output2"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// 각 테스트 전 초기화
handler.Clear()
got := handler.Process(tc.input)
if got != tc.want {
t.Errorf("got %v, want %v", got, tc.want)
}
})
}
}
7. 성능 최적화
// 고빈도 호출 함수에서 재사용
type Processor struct {
tempBuffer []int
}
func NewProcessor() *Processor {
return &Processor{
tempBuffer: make([]int, 0, 1000), // 미리 할당
}
}
func (p *Processor) Process(data []int) []int {
// 임시 버퍼 초기화 (재할당 없음)
clear(p.tempBuffer)
p.tempBuffer = p.tempBuffer[:0]
// 처리 로직
for _, v := range data {
if v > 0 {
p.tempBuffer = append(p.tempBuffer, v*2)
}
}
// 결과 복사
result := make([]int, len(p.tempBuffer))
copy(result, p.tempBuffer)
return result
}
8. 조건부 clear
func (c *Cache) ClearIfFull() bool {
if len(c.data) >= c.maxSize {
clear(c.data)
return true
}
return false
}
func (b *Buffer) ClearIfStale(maxAge time.Duration) bool {
if time.Since(b.lastUpdate) > maxAge {
clear(b.data)
b.data = b.data[:0]
return true
}
return false
}
정리
- 기본: 맵의 모든 항목 제거, 슬라이스 요소를 제로 값으로
- 성능: 재할당보다 빠름, 메모리 재사용
- 메모리: 용량 유지, GC 압력 감소
- vs 다른 방법: clear > 재할당 > 반복문
- 슬라이스: clear + [:0]로 완전 초기화
- 포인터: clear로 nil 설정하여 메모리 누수 방지
- 실전: 캐시, 풀, 배치, 통계, 워커
- 실수: nil 체크 누락, 길이 변경 기대, 메모리 누수, 동시성
- 베스트: nil 체크, 캡슐화, 재사용, 풀, 문서화, 테스트