golang pprof性能调试:寻找memory瓶颈
1、前置
pprof的使用与输出列解析看姐妹篇:golang pprof性能调试:寻找cpu瓶颈
2、引入pprof到程序中,以调试memory瓶颈
给程序加入:
import _ "net/http/pprof"
go func() {
http.ListenAndServe("0.0.0.0:9999", nil)
}()
示例:随机拼接字符串
package main
import (
"math/rand"
"net/http"
_ "net/http/pprof"
)
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func randomString(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
func concat(n int) string {
s := ""
for i := 0; i < n; i++ {
s += randomString(n)
}
return s
}
func main() {
go func() {
http.ListenAndServe("0.0.0.0:9999", nil)
}()
for {
concat(100)
}
}
3、pprof命令行交互式查看程序最消耗memory的地方
1、启动上述程序:
go run main.go
2、访问pprof暴露的memory debug api接口,收集30s内的memory数据:
go tool pprof http://localhost:9999/debug/pprof/heap\?seconds\=30
3、在交互命令中,使用top,list来查看最消耗memory的地方
go tool pprof http://localhost:9999/debug/pprof/heap\?seconds\=30
Fetching profile over HTTP from http://localhost:9999/debug/pprof/heap?seconds=30
Saved profile in /Users/zejia.lu/pprof/pprof.alloc_objects.alloc_space.inuse_objects.inuse_space.001.pb.gz
Type: inuse_space
Time: Nov 27, 2022 at 12:40pm (CST)
Duration: 30s, Total samples = 1MB
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for -1026.68kB, 100% of 1026.68kB total
flat flat% sum% cum cum%
514.63kB 50.13% 50.13% -1026.68kB 100% main.concat
512.05kB 49.87% 100% -512.05kB 49.87% main.randomString
0 0% 100% -1026.68kB 100% main.main
0 0% 100% -1026.68kB 100% runtime.main
(pprof) list main.randomString
Total: 1MB
ROUTINE ======================== main.randomString in /Users/zejia.lu/memory_pprof/main.go
512.05kB 512.05kB (flat, cum) 49.87% of Total
. . 11:func randomString(n int) string {
. . 12: b := make([]byte, n)
. . 13: for i := range b {
. . 14: b[i] = letterBytes[rand.Intn(len(letterBytes))]
. . 15: }
512.05kB 512.05kB 16: return string(b)
. . 17:}
. . 18:
. . 19:func concat(n int) string {
. . 20: s := ""
. . 21: for i := 0; i < n; i++ {
4、pprof ui方式查看程序最消耗memory的地方
1、调试时指定通过ui方式查看,ui地址为9900端口,要调试的服务暴露的pprof端口为9999:
go tool pprof -http=":9900" http://localhost:9999/debug/pprof/heap -inuse_objects
2、访问9900端口查看ui界面进行可视化调试:
查看最消耗memory的地方:可以看到我们的concat逻辑函数就是memoory高消耗的地方
5、pprof ui方式查看memory消耗拓扑图
6、pprof ui方式查看memory消耗火焰图
7、查看其它类型的memroy,上述例子查看的是:inuse_space
inuse_space — 已分配但尚未释放的内存空间
inuse_objects——已分配但尚未释放的对象数量
alloc_space — 分配的内存总量(已释放的也会统计)
alloc_objects — 分配的对象总数(无论是否释放)
8、查看所有曾经申请过的内存总量
go tool pprof -http=":9900" http://localhost:9999/debug/pprof/allocs
结果:
9、如何调试其它方面的内容:
查看程序暴露的/debug/pprof/ api
http://127.0.0.1:9999/debug/pprof/
查看有哪些方面的调试内容:
常用调试示例:
# 获取30秒的CPU profiling
curl -o cpu.bin http://localhost:6060/debug/pprof/profile
# 获取5秒的执行跟踪(有一定性能影响)
curl -o trace.bin http://localhost:6060/debug/pprof/trace?seconds=5
# 获取内存使用的profile
curl -o heap.bin http://localhost:6060/debug/pprof/heap
# 获取正在运行的goroutines列表
curl -o goroutines.txt http://localhost:6060/debug/pprof/goroutine?debug=2
# 获取被blocking的goroutine列表
curl -o goroutines-blocking.txt http://localhost:6060/debug/pprof/block
如何分析火焰图
火焰图的调用顺序从下到上,每个方块代表一个函数,它上面一层表示这个函数会调用哪些函数,方块的大小代表了占用 CPU 使用时长长短。