Go中的内存泄漏通常是指在运行过程中全局变量所分配的内存越来越多,而没有释放。利用自带的pprof工具可以很方便的排查这类问题。下面的示例为了简化问题,仅仅是在函数调用过程中为两个全局变量分别赋值一个200MB和100MB的切片。
示例代码pprof.go
调用关系:
- handler1 > func1_1 > func1_2 (创建200MB的切片) > func1_3
- handler2 > func2_1 > func2_2 > func2_3(创建100MB的切片)
package main
import (
"fmt"
"net/http"
_ "net/http/pprof"
"runtime"
)
var container []byte
var container2 []byte
func func1_3() {
fmt.Printf("handler1 %d\n", len(container))
}
func func1_2() {
container = make([]byte, 200*1024*1024)
for i := 0; i < 200*1024*1024; i++ {
container[i] = 0
}
func1_3()
}
func func1_1() {
func1_2()
}
func func2_3() {
container2 = make([]byte, 100*1024*1024)
for i := 0; i < 100*1024*1024; i++ {
container2[i] = 0
}
fmt.Printf("handler2 %d\n", len(container2))
}
func func2_2() {
func2_3()
}
func func2_1() {
func2_2()
}
func handler1(w http.ResponseWriter, r *http.Request) {
func1_1()
runtime.GC()
}
func handler2(w http.ResponseWriter, r *http.Request) {
func2_1()
runtime.GC()
}
func main() {
http.HandleFunc("/test1", handler1)
http.HandleFunc("/test2", handler2)
http.ListenAndServe(":8888", nil)
}
请求/test1和/test2各一次,等待一段时间直到进程的RSS不再下降:
top命令:
322m大致等于container,container2两个全局变量的内存占用之和。
执行go tool pprof -inuse_space http://127.0.0.1:8888/debug/pprof/heap进入交互模式:
结果排序方式:
- 首先按函数所申请的内存大小倒排序,
- 然后按调用顺序从下到上,
- 如果内存大小相同又没有调用关系的则按字符串排序,如func1_1排在func2_1前面。
可以看到,main.func1_2和main.func2_3是申请内存最多的两个函数,这与前面的预期是一致的。另外,虽然func1_2还调用了func1_3但是func1_3不会在这里显示。
在交互模式下,输入list命令 + 函数名称可以进一步看到内存申请在函数中的具体位置。这里必须保证go的源文件在执行go tool pprof命令的机器上能够找得到,否则会报“Error: Could not find file”。
到这里,结合着代码来看基本上就能找到内存泄漏的点。如果要进一步查看调用关系,可以把上述top命令的输出结果导出为图片:
go tool pprof -inuse_space -png http://127.0.0.1:8888/debug/pprof/heap > heap.png
这条命令需要依赖Graphviz,在Centos上可以直接用yum install来安装:
yum install graphviz
windows需要下载安装文件graphviz-2.38.msi,安装完毕后配置系统环境变量C:\Program Files (x86)\Graphviz2.38\bin
dot -v有返回则安装成功。
[root@dev tutorial]# dot -v
dot - graphviz version 2.26.0 (20091210.2329)
heap.png
相关文章: