Golang中基于StackInUse调查疑似内存泄漏问题

Golang中基于StackInUse调查疑似内存泄漏问题 我正在尝试查找应用程序中的内存泄漏。 我已向日志中添加了一些运行时内存统计信息(堆和栈统计信息)。 我可以看到,从应用程序的角度来看,m.StackInuse 会时不时地增加,且无明显原因。 以下是一些额外的运行时内存统计信息(在同一时间点获取): StackInUse 56MB HeapSys: 147MB. HeapAlloc: 97MB

虽然 StackInUseHeapSys 似乎会时不时增加,但 HeapAlloc 在应用程序运行的6小时内基本保持不变。 StackInUseHeapSys 之间似乎存在一些相关性,但随着时间的推移,StackInUse 的增加量超过了 HeapSys。(HeapSys 是否包含 StackInUse?)

除了添加运行时内存日志外,我还定期创建堆配置文件转储。 我的问题是,当使用 pprof 分析堆时,它没有给我任何关于 StackInUse 为何如此之高的线索。 pprofinuse_space 显示: “Showing nodes accounting for 55.31MB, 93.25% of 59.31MB total” 这个总 MB 数代表什么?它似乎与 HeapAllocHeapSysStackInUse 都不匹配。 pprof 堆配置文件是否包含 StackInUse

我真的需要弄清楚泄漏来自哪里,但在查看了许多地方之后,内存统计信息对我来说仍然不清楚,而且我也不清楚 pprof 堆配置文件真正代表的是哪种内存统计信息。 请注意,我也记录了 HeapObjects 计数,并且在那里没有看到任何泄漏……只是 StackInUseHeapSys 会时不时增加。

请注意,我也在使用 Cgo,但我的理解是 Cgo 内存分配不会反映在运行时内存统计信息中。这是正确的吗?并且我假设如果运行时内存统计信息在增加,这肯定是因为 Go 代码而不是 C 代码? 我希望我已经表述清楚了,但添加了一张不同内存统计信息的截图。 图中用红色标记了 StackInUse 增加的时间点。(我目前的理解可能是错误的,即 StackInUse 不包含在 HeapSys 中,这就是它们在图中堆叠显示的原因)

Untitled


更多关于Golang中基于StackInUse调查疑似内存泄漏问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中基于StackInUse调查疑似内存泄漏问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中,StackInUse 是goroutine栈内存的一部分,而 HeapSys 是堆内存。StackInUse 不包含在 HeapSys 中,它们是独立的内存区域。pprof 堆配置文件通常只包含堆内存分配,不包括栈内存,因此 pprofinuse_space 显示的总MB数通常对应 HeapAlloc,而不是 StackInUse

StackInUse 增加可能表明goroutine数量增长或每个goroutine的栈大小增加。这可能是由以下原因引起的:

  1. 持续创建的goroutine未退出,导致栈内存累积。
  2. 递归或深度函数调用导致栈扩容。

以下是一个示例,展示如何监控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数量的日志,并使用 pprofgoroutine 配置文件:

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)来检测。

回到顶部