Skip to content
On this page

内存管理

Go 使用自动垃圾回收(GC)管理内存。本章将详细介绍 Go 的内存管理机制。

堆和栈

栈内存

go
func stackExample() {
    // 局部变量分配在栈上
    x := 10
    y := 20
    sum := x + y
    
    // 函数返回后自动释放
}

堆内存

go
func heapExample() *int {
    // 返回局部变量的指针,分配在堆上
    x := 10
    return &x
}

垃圾回收

GC 基础

Go 使用三色标记清除算法进行垃圾回收。

go
func gcExample() {
    // 创建对象
    obj := &MyStruct{}
    
    // 使用对象
    obj.Method()
    
    // obj 不再被引用,GC 会回收
}

GC 调优

go
func main() {
    // 设置 GC 目标百分比
    debug.SetGCPercent(100)
    
    // 手动触发 GC
    runtime.GC()
    
    // 你的代码...
}

内存泄漏

Goroutine 泄漏

go
// 错误示例:goroutine 泄漏
func leak() {
    ch := make(chan int)
    
    go func() {
        <-ch  // 永远阻塞
    }()
    
    // ch 永远不会被关闭
}

// 正确示例
func noLeak() {
    ch := make(chan int)
    
    go func() {
        select {
        case <-ch:
        case <-time.After(time.Second):
        }
    }()
    
    close(ch)
}

循环引用

go
type Node struct {
    Next *Node
    Data interface{}
}

// 错误示例:循环引用
func createCycle() {
    a := &Node{Data: "A"}
    b := &Node{Data: "B"}
    a.Next = b
    b.Next = a  // 循环引用
    
    // 需要手动打破循环
    a.Next = nil
    b.Next = nil
}

全局变量

go
// 错误示例:全局变量累积
var cache = make(map[string]interface{})

func addToCache(key string, value interface{}) {
    cache[key] = value  // 永远增长
}

// 正确示例:使用 LRU
type LRUCache struct {
    capacity int
    items    map[string]*list.Element
    lru       *list.List
}

func NewLRUCache(capacity int) *LRUCache {
    return &LRUCache{
        capacity: capacity,
        items:    make(map[string]*list.Element),
        lru:       list.New(),
    }
}

内存优化技巧

减少分配

go
// 不推荐:频繁分配
func process(data []byte) {
    for i := 0; i < 1000; i++ {
        buf := make([]byte, 1024)
        copy(buf, data)
        // 处理...
    }
}

// 推荐:重用缓冲区
func processOptimized(data []byte) {
    buf := make([]byte, 1024)
    for i := 0; i < 1000; i++ {
        copy(buf, data)
        // 处理...
    }
}

对象池

go
var objectPool = sync.Pool{
    New: func() interface{} {
        return &MyStruct{}
    },
}

func getObject() *MyStruct {
    return objectPool.Get().(*MyStruct)
}

func putObject(obj *MyStruct) {
    obj.Reset()
    objectPool.Put(obj)
}

预分配

go
// 不推荐:动态增长
func createSlice(n int) []int {
    slice := []int{}
    for i := 0; i < n; i++ {
        slice = append(slice, i)
    }
    return slice
}

// 推荐:预分配
func createSliceOptimized(n int) []int {
    slice := make([]int, n)
    for i := 0; i < n; i++ {
        slice[i] = i
    }
    return slice
}

内存分析

pprof 内存分析

bash
# 采集堆数据
go tool pprof http://localhost:6060/debug/pprof/heap

# 查看分配
(pprof) top

# 查看特定函数
(pprof) list functionName

# 生成火焰图
(pprof) web

内存统计

go
func printMemStats() {
    var m runtime.MemStats
    runtime.ReadMemStats(&m)
    
    fmt.Printf("Alloc = %v MiB\n", m.Alloc/1024/1024)
    fmt.Printf("TotalAlloc = %v MiB\n", m.TotalAlloc/1024/1024)
    fmt.Printf("Sys = %v MiB\n", m.Sys/1024/1024)
    fmt.Printf("NumGC = %v\n", m.NumGC)
}

内存对齐

结构体对齐

go
// 不推荐:浪费内存
type BadStruct struct {
    a bool    // 1 byte
    b int64   // 8 bytes
    c bool    // 1 byte
    d int64   // 8 bytes
}
// 总大小: 32 bytes

// 推荐:优化对齐
type GoodStruct struct {
    b int64   // 8 bytes
    d int64   // 8 bytes
    a bool    // 1 byte
    c bool    // 1 byte
}
// 总大小: 16 bytes

检查对齐

go
func printStructSize() {
    fmt.Printf("BadStruct: %d bytes\n", unsafe.Sizeof(BadStruct{}))
    fmt.Printf("GoodStruct: %d bytes\n", unsafe.Sizeof(GoodStruct{}))
}

内存最佳实践

1. 避免不必要的指针

go
// 不推荐:使用指针
type Data struct {
    value *int
}

// 推荐:使用值
type DataOptimized struct {
    value int
}

2. 使用值类型

go
// 不推荐:切片的切片
type Matrix [][]float64

// 推荐:一维数组
type MatrixOptimized struct {
    data []float64
    rows int
    cols int
}

3. 及时释放资源

go
func processFile(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer file.Close()  // 确保释放
    
    // 处理文件...
    return nil
}

内存泄漏检测

使用 pprof

bash
# 对比两个时间点的内存
go tool pprof -base=base.prof current.prof

使用 runtime

go
func checkGoroutines() {
    fmt.Printf("Goroutines: %d\n", runtime.NumGoroutine())
}

func checkMemory() {
    var m runtime.MemStats
    runtime.ReadMemStats(&m)
    fmt.Printf("Alloc: %d MB\n", m.Alloc/1024/1024)
}

总结

  • 理解堆和栈的区别
  • 了解 GC 的工作原理
  • 避免常见的内存泄漏
  • 使用对象池减少分配
  • 优化结构体对齐
  • 定期分析内存使用

下一章:CGO

基于 MIT 许可发布