Golang中基于StackInUse调查疑似内存泄漏问题
Golang中基于StackInUse调查疑似内存泄漏问题
我正在尝试查找应用程序中的内存泄漏。
我已向日志中添加了一些运行时内存统计信息(堆和栈统计信息)。
我可以看到,从应用程序的角度来看,m.StackInuse 会时不时地增加,且无明显原因。
以下是一些额外的运行时内存统计信息(在同一时间点获取):
StackInUse 56MB
HeapSys: 147MB.
HeapAlloc: 97MB
虽然 StackInUse 和 HeapSys 似乎会时不时增加,但 HeapAlloc 在应用程序运行的6小时内基本保持不变。
StackInUse 和 HeapSys 之间似乎存在一些相关性,但随着时间的推移,StackInUse 的增加量超过了 HeapSys。(HeapSys 是否包含 StackInUse?)
除了添加运行时内存日志外,我还定期创建堆配置文件转储。
我的问题是,当使用 pprof 分析堆时,它没有给我任何关于 StackInUse 为何如此之高的线索。
pprof 的 inuse_space 显示:
“Showing nodes accounting for 55.31MB, 93.25% of 59.31MB total”
这个总 MB 数代表什么?它似乎与 HeapAlloc、HeapSys 或 StackInUse 都不匹配。
pprof 堆配置文件是否包含 StackInUse?
我真的需要弄清楚泄漏来自哪里,但在查看了许多地方之后,内存统计信息对我来说仍然不清楚,而且我也不清楚 pprof 堆配置文件真正代表的是哪种内存统计信息。
请注意,我也记录了 HeapObjects 计数,并且在那里没有看到任何泄漏……只是 StackInUse 和 HeapSys 会时不时增加。
请注意,我也在使用 Cgo,但我的理解是 Cgo 内存分配不会反映在运行时内存统计信息中。这是正确的吗?并且我假设如果运行时内存统计信息在增加,这肯定是因为 Go 代码而不是 C 代码?
我希望我已经表述清楚了,但添加了一张不同内存统计信息的截图。
图中用红色标记了 StackInUse 增加的时间点。(我目前的理解可能是错误的,即 StackInUse 不包含在 HeapSys 中,这就是它们在图中堆叠显示的原因)

更多关于Golang中基于StackInUse调查疑似内存泄漏问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang中基于StackInUse调查疑似内存泄漏问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go中,StackInUse 是goroutine栈内存的一部分,而 HeapSys 是堆内存。StackInUse 不包含在 HeapSys 中,它们是独立的内存区域。pprof 堆配置文件通常只包含堆内存分配,不包括栈内存,因此 pprof 的 inuse_space 显示的总MB数通常对应 HeapAlloc,而不是 StackInUse。
StackInUse 增加可能表明goroutine数量增长或每个goroutine的栈大小增加。这可能是由以下原因引起的:
- 持续创建的goroutine未退出,导致栈内存累积。
- 递归或深度函数调用导致栈扩容。
以下是一个示例,展示如何监控goroutine数量和栈内存:
package main
import (
"fmt"
"runtime"
"time"
)
func monitor() {
for {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("StackInUse: %v MB, HeapAlloc: %v MB, Goroutines: %v\n",
m.StackInuse/1024/1024,
m.HeapAlloc/1024/1024,
runtime.NumGoroutine())
time.Sleep(5 * time.Second)
}
}
func leakyGoroutine() {
time.Sleep(time.Hour) // 模拟长期运行的goroutine
}
func main() {
go monitor()
for i := 0; i < 1000; i++ {
go leakyGoroutine() // 持续创建goroutine
}
select {}
}
要调查 StackInUse 增加,可以添加goroutine数量的日志,并使用 pprof 的 goroutine 配置文件:
import _ "net/http/pprof"
go func() {
http.ListenAndServe("localhost:6060", nil)
}()
然后访问 http://localhost:6060/debug/pprof/goroutine?debug=2 查看所有goroutine的堆栈跟踪,识别泄漏的goroutine。
对于Cgo,其内存分配确实不反映在Go运行时内存统计中。如果 StackInUse 增加,这通常是由Go代码引起的,例如goroutine泄漏或栈扩容。Cgo内存泄漏需要通过其他工具(如Valgrind)来检测。

