Files
bulma/gc_memory_leak.go
T

101 lines
2.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 MBGC次数:%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 MBGC次数:%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 MBGC次数:%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 MBGC次数:%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)
}