7.1 内存管理
内存分配机制
Go 内存分配器 (基于 tcmalloc):
内存层次:
1. sync.Pool - 对象池 (最快)
2. mspan - 小对象分配 (2KB - 32KB)
3. mheap - 大对象分配 (>32KB)
4. mmap - 直接向系统申请
分配策略:
- 小对象 (< 32KB): 从 mspan 分配
- 大对象 (> 32KB): 直接从 mheap 分配
- 超大对象 (> 32MB): 直接向系统 mmap
逃逸分析:
- 编译器决定变量分配在栈上还是堆上
- 栈分配更快,自动释放
- 堆分配需要 GC查看内存统计
go
import "runtime"
func printMemStats() {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("Alloc = %v KB\n", m.Alloc/1024)
fmt.Printf("TotalAlloc = %v KB\n", m.TotalAlloc/1024)
fmt.Printf("Sys = %v KB\n", m.Sys/1024)
fmt.Printf("NumGC = %v\n", m.NumGC)
}减少内存分配
go
// 不好:循环内分配
func badAlloc() []byte {
var result []byte
for i := 0; i < 1000; i++ {
result = append(result, make([]byte, 100)...)
}
return result
}
// 好:预设容量
func goodAlloc() []byte {
result := make([]byte, 0, 1000*100)
for i := 0; i < 1000; i++ {
// 使用预分配的容量
}
return result
}
// 使用 sync.Pool
var bufferPool = sync.Pool{
New: func() interface{} {
return make([]byte, 1024)
},
}
func usePool() {
buf := bufferPool.Get().([]byte)
defer bufferPool.Put(buf)
// 使用 buf
}逃逸分析
go
// 逃逸到堆
func escapeToHeap() *int {
x := 42
return &x // x 逃逸到堆上
}
// 不逃逸 (栈分配)
func noEscape() int {
x := 42
return x
}
// 闭包捕获 (逃逸)
func closureEscape() func() int {
x := 42
return func() int {
return x
}
}
// 查看逃逸分析
// go build -gcflags="-m" main.go垃圾回收 (GC)
GC 算法 (并发标记 - 清扫):
GC 阶段:
1. 标记准备 (STW, 短暂)
2. 并发标记 (应用程序继续运行)
3. 标记结束 (STW, 短暂)
4. 并发清扫 (应用程序继续运行)
GC 调优:
1. 调整 GOGC (默认 100)
export GOGC=50 # 更频繁 GC,更低内存
export GOGC=200 # 更少 GC,更高内存
2. 减少分配
- 预设容量
- 对象复用
- 避免逃逸
3. 使用 sync.Pool内存优化技巧
go
// 1. 字符串拼接
// 不好:O(n²)
func badConcat(parts []string) string {
result := ""
for _, p := range parts {
result += p
}
return result
}
// 好:使用 strings.Builder
func goodConcat(parts []string) string {
var sb strings.Builder
for _, p := range parts {
sb.WriteString(p)
}
return sb.String()
}
// 2. Map 预分配
m := make(map[int]int, 1000)
// 3. 零拷贝
func zeroCopy(data []byte, start, end int) []byte {
return data[start:end]
}
// 4. 结构体字段对齐 (按大小排序)
type GoodStruct struct {
B int64 // 8 bytes
A bool // 1 byte
C bool // 1 byte
} // 16 bytes