package main import ( "fmt" "log" "net/http" _ "net/http/pprof" "runtime" "sync" "time" ) // leakSlice 全局切片,持续追加数据导致内存泄漏 var leakSlice [][]byte // generateLeak 模拟内存泄漏 func generateLeak() { for { // 每次分配10MB,追加到全局切片(永久引用,无法回收) leakSlice = append(leakSlice, make([]byte, 10*1024*1024)) time.Sleep(100 * time.Millisecond) // 打印当前内存状态 var stats runtime.MemStats runtime.ReadMemStats(&stats) fmt.Printf("当前堆内存:%d MB,GC次数:%d\n", stats.HeapAlloc/1024/1024, stats.NumGC) } } func generateLeak1() { var maxMem uint64 = 500 // 最大允许占用500MB内存 for { leakSlice = append(leakSlice, make([]byte, 10*1024*1024)) time.Sleep(100 * time.Millisecond) var stats runtime.MemStats runtime.ReadMemStats(&stats) curMem := stats.HeapAlloc / 1024 / 1024 fmt.Printf("当前堆内存:%d MB,GC次数:%d\n", curMem, stats.NumGC) // ✅ 核心修复:内存超限后,置空全局切片,切断引用 if curMem >= maxMem { leakSlice = nil // 关键一行!置空后,原切片的所有[]byte都失去引用 runtime.GC() // 手动触发GC,立刻回收内存 fmt.Println("=== 内存超限,已释放所有内存 ===") } } } func generateLeak2() { maxLen := 20 // 最多保留20个10MB切片,总内存上限200MB for { leakSlice = append(leakSlice, make([]byte, 10*1024*1024)) // ✅ 核心修复:超过最大长度,删除头部旧数据,释放引用 if len(leakSlice) > maxLen { leakSlice = leakSlice[1:] // 切片切片,丢弃第一个元素,GC会回收它 } time.Sleep(100 * time.Millisecond) var stats runtime.MemStats runtime.ReadMemStats(&stats) fmt.Printf("当前堆内存:%d MB,GC次数:%d,切片长度:%d\n", stats.HeapAlloc/1024/1024, stats.NumGC, len(leakSlice)) } } // ✅ 定义10MB切片的对象池 var needBytePool = sync.Pool{ New: func() interface{} { return make([]byte, 10*1024*1024) // 每次创建10MB切片 }, } // 无内存泄漏!复用内存,不持有永久引用 func generateLeak3() { for { // 从池子里拿切片,复用内存 buf := needBytePool.Get().([]byte) // 模拟业务使用 _ = buf // 使用完放回池子,供后续复用,释放引用 needBytePool.Put(buf[:0]) time.Sleep(100 * time.Millisecond) var stats runtime.MemStats runtime.ReadMemStats(&stats) fmt.Printf("当前堆内存:%d MB,GC次数:%d\n", stats.HeapAlloc/1024/1024, stats.NumGC) } } func main() { // 启动pprof服务 go func() { log.Println("pprof服务启动:http://localhost:6060/debug/pprof/") log.Fatal(http.ListenAndServe(":6060", nil)) }() // 模拟内存泄漏 go generateLeak3() // 运行60秒,便于诊断 time.Sleep(60 * time.Second) }