101 lines
2.8 KiB
Go
101 lines
2.8 KiB
Go
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)
|
||
}
|