什么是pprof?
pprof(性能分析库)是 Go 语言标准库中的一个性能分析工具。它提供了一系列的工具和函数,用于帮助开发人员识别和定位应用程序的性能瓶颈。
pprof 可以对 CPU 使用情况、内存分配、阻塞等进行分析,并生成可视化报告,以便更好地理解应用程序的性能特征。
具体而言,pprof 提供了以下功能:
- CPU Profiling:通过监测线程的运行时间来识别 CPU 瓶颈,并生成火焰图(flame graph)以可视化代码执行路径。
- Heap Profiling:用于分析内存分配和回收情况,发现内存泄漏或过度分配的问题,生成堆分析图(heap profile)。
- Goroutine Profiling:用于分析和跟踪 Goroutine 的创建和销毁,帮助查找 Goroutine 泄漏和无法释放的问题。
- Block Profiling:用于跟踪阻塞事件,识别 Goroutine 在等待互斥锁、通道操作或其他同步原语时的延迟。
pprof 还提供了与分析和 profile 数据交互的 API,可以将性能数据输出到文件或网络,以及通过 Web 页面进行可视化展示和分析。
要使用 pprof 进行性能分析,可以使用 net/http/pprof 包,它提供了与 HTTP 服务器集成的功能。通过导入该包并注册相应的路由,可以通过 HTTP 请求来触发性能分析和获取分析结果。
总而言之,pprof 是一个强大的性能分析工具,可以帮助开发人员在开发和调试过程中定位和解决应用程序的性能问题。
pprof的体验
首先先在github.com/wolfogre/go… 上将老师的代码给code下来。 它会在电脑上占用大量内存,用来给我们体验pprof工具检查各项指标。
因为pprof提供了Http服务器的功能,所以运行代码后,我们能在http://localhost:6060/debug/pprof/ 上看各项指标。
Cpu
在终端输入go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"
即可看到cpu在这十秒内的占用情况
接着使用topN
即可看的占用最多的函数是github.com/wolfogre/go-pprof-practice/animal/felidae/tiger.(*Tiger).Eat。
参数含义
- flat: 当前函数自身占用CPU的时间
- flat%:当前函数占总CPU时间的百分比
- sum%: 前若干函数的flat%之和
- cum:当前函数及其调用链占用CPU总时间
- cum%:当前函数及其调用链占总CPU时间的百分比
比较重要的是flat%和cum%。它们从不同层面反应函数的占用CPU时间。
当flat==cum时,表示该函数中没有调用其他函数。 当flat==0时,表示该函数里只有其他函数的调用。
使用list命令
list Eat
根据指定正则表达式来查找到对应的代码行。
因为这段代码占用太多内存,我们将它注释掉
还可以指定所查询的代码行的范围
list Eat:1,10
就是查找1~10行的范围
使用list Eat.*会查询所有Eat开头的代码,包括EatApple,EatBanana,EatGrape.
使用list Eat -len=20输出指定的第20行
web命令
如果像我一样,提示这段文字
该错误信息是因为go tool pprof 在使用web命令打开图形界面时,会使用Graphviz来生成调用图,而因为未安装Graphviz软件,将导致的无法执行dot命令。
OS X和Ubuntu可以直接使用brew和apt-get install graphviz
Windows用户可以下载zip包,然后将解压后的目录添加到Path环境变量中。
下载地址:www.graphviz.org/download/
安装好后,就可以看到调用关系了
Heap-堆内存
在终端输入 go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"
浏览器会跳转到http://localhost:8080/ui/
如图:
在这个页面里view->top
可以看到与
topN命令一样的数据结果。
在图中
Steal函数自身占用了768MB- 剩余3个函数的
Flat属性为0,表示在他们的函数中调用了其他的函数,所以整个函数的调用链占用&=768MB,这里推测调用的函数是Steal
通过view->source可以看到
Live中调用了Steal函数,所以进入代码中,寻找Steal,找出问题所在。
我们发现,这段代码在一直申请切片,就会导致占用很大的内存,所以我们注释掉
重新运行代码
会发现占比下降了许多。
Sample工具
pprof工具的Web页面中的sample部分主要包含4个不同维度的内存分析数据
-
inuse_space 当前使用的内存大小(默认使用)
它展示当前应用实时正在使用的内存大小,可以看到每个函数占用的内存情况。
-
alloc_space 累计分配的内存大小
它展示从应用启动至当前,各函数累计申请过的内存总大小,包括之前已释放的内存。
-
inuse_objects 当前使用的对象数量
它统计每个函数当前占用的对象数量,反映内存使用情况。
-
alloc_objects 累计分配的对象数量
它统计每个函数累计申请过的对象总数,包含之前已释放的对象。
我们进入alloc_space
会发现*Dog占用了许多内存,我们同样也可以找到对应代码给注释掉。
goroutine命令
处理完堆内存后,我们再次进入 /debug/pprof/,在该浏览器查看各项指标,然后我们看到goroutine有6个(每台机器运行结果不一定相同),goroutine过多也会导致内存泄露。
再输入go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/goroutine"
选择view->Flame Graph
每一块代表一个函数,越长代表占用CPU的时间更长
我们可以看到*Wolf占用了最长的时间
进入view->source搜索Wolf
从图中发现,每创建一个协程,将睡眠30秒,浪费了太多时间在阻塞状态
同样将代码注释掉
重新运行代码
发现goroutine变少。
mutex 锁
在终端输入
go tool pprof -http=:8080 http://localhost:6060/debug/pprof/mutex
随即跳转到localhost:8080/ui/
发现Wolf中包含一个Howl函数,里面进行了睡眠操作,睡眠了1s,导致进程等待了1s.所以我们将它注释掉
block
在终端输入go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/block"
随即跳转到localhost:8080/ui/
在view->source中找到Cat
可以看到Pee()函数足足阻塞了1s
同样注释掉这一段
最后再输入go tool pprof -http=:8080 http://localhost:6060/debug/pprof/block
结束。