1. 启用 pprof
pprof
是 Go 自带的性能分析工具,用于检查内存使用、CPU 时间和 Goroutines 数量。
(1)在程序中启用 pprof
确保在程序中加入以下代码,不一定要使用init方法,也可以在任意位置添加如下代码,在需要的时候运行即可。
Go
import (
_ "net/http/pprof"
"log"
"net/http"
)
func init() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
}
(2)捕获内存剖析数据
运行程序后,访问以下 URL:
- 当前堆内存 :
http://localhost:6060/debug/pprof/heap
- 历史分配对象 :
http://localhost:6060/debug/pprof/allocs
- Goroutines 数量 :
http://localhost:6060/debug/pprof/goroutine?debug=2
使用 go tool pprof
对程序的内存情况进行分析:
bash
go tool pprof http://localhost:6060/debug/pprof/heap
在交互模式下:
top
:查看内存占用最多的函数。web
:生成调用图(需要安装 Graphviz)。
- 监控运行时内存状态
通过 runtime
包动态打印内存使用情况:
Go
package main
import (
"fmt"
"runtime"
"time"
)
func printMemStats() {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("HeapAlloc = %v MiB", m.HeapAlloc/1024/1024)
fmt.Printf("\tTotalAlloc = %v MiB", m.TotalAlloc/1024/1024)
fmt.Printf("\tSys = %v MiB", m.Sys/1024/1024)
fmt.Printf("\tNumGC = %v\n", m.NumGC)
}
func main() {
for {
printMemStats()
time.Sleep(5 * time.Second)
}
}
三、定位高内存占用原因
1. 检查 Goroutine 泄漏
使用 pprof 检查 Goroutines 的数量:
Go
curl http://localhost:6060/debug/pprof/goroutine?debug=2
如果数量持续增加,可能存在 Goroutine 泄漏问题。重点检查:
- 未退出的协程(如
select
未处理退出信号)。 - HTTP 请求未正确关闭。
2. 检查内存分配
通过 pprof/heap
,查看哪些函数分配了最多的内存。可能的原因包括:
- 切片或 map 容量分配过大。
- 循环中频繁创建临时对象。
- 缓存未及时释放(如大数据存储在全局变量)。
3. 大对象问题
Go 对大对象的分配较为敏感,常见问题:
- 切片扩容频率高。
- 单个字符串或 JSON 对象过大。
优化措施:
- 使用
bytes.Buffer
或sync.Pool
。 - 控制切片初始容量,避免频繁扩容。
五、常见问题与解决
1. Goroutine 泄漏
- 原因:未退出的 Goroutine 占用内存。
- 解决:确保所有 Goroutine 有退出条件,例如:
Go
ctx, cancel := context.WithCancel(context.Background())
go func() {
<-ctx.Done()
return
}()
cancel()
2. 切片无限增长
- 原因:切片在循环中不断扩容。
- 解决:预分配容量,避免不必要的扩容。
3. 长生命周期对象
- 原因:全局变量或缓存未释放。
- 解决 :定期清理缓存,或使用弱引用(如
sync.Pool
)。
4. 垃圾回收(GC)问题
- 现象:GC 频率过低或过高导致内存耗尽。
- 优化:调整 GC 百分比:
Go
debug.SetGCPercent(50) // 提高 GC 频率