profile

线上性能问题的定位和优化是程序员进阶的必经之路,定位问题的方式有多种多样,常见的有观察线程栈、排查日志和做性能分析。性能分析(profile)作为定位性能问题的大杀器,它可以收集程序执行过程中的具体事件,并且对程序进行抽样统计,从而能更精准的定位问题。本文会以 go 语言的 pprof 工具为例,分享两个线上性能故障排查过程,希望能通过本文使大家对性能分析有更深入的理解。



golang 对于 profiling 的支持比较完善,如代码所示,只需要简单的引入 net/http/pprof 这个包,然后在 main 函数里启动一个 http server 就相当于给线上服务加上 profiling 了,通过访问 8005 这个 http 端口就可以对程序做采样分析。



服务上开启 pprof 之后,在本地电脑上使用 go tool pprof 命令,可以对线上程序发起采样请求,golang pprof 工具会把采样结果绘制成一个漂亮的前端页面供人们排查问题。



等到故障再次复现时,我们首先对 cpu 性能进行采样分析:



brew install graphviz # 安装 graphviz,只需要安装一次就行了
go tool pprof -http=:1234 http://your-prd-addr:8005/debug/pprof/profile?seconds=30
打开 terminal,输入上面命令,把命令中的 your-prd-addr 改成线上某台机器的地址,然后回车等待 30 秒后,会自动在浏览器中打开一个页面,这个页面包含了刚刚 30 秒内对线上 cpu 占用情况的一个概要分析。点击左上角的 View 选择 Flame graph,会用火焰图(Flame graph)来显示 cpu 的占用情况

在火焰图的右边,有个让我比较在意的点是 runtime.gcBgMarkWorker 函数,这个函数是 golang 垃圾回收相关的函数,用于标记(mark)出所有是垃圾的对象。一般情况下此函数不会占用这么多的 cpu,出现这种情况一般都是内存 gc 问题,但是刚刚的监控上看内存占用只比平常多了几百 M,并没有特别高又是为什么呢?原因是影响 GC 性能的一般都不是内存的占用量,而是对象的数量。举例说明,10 个 100m 的对象和一亿个 10 字节的对象占用内存几乎一样大,但是回收起来一亿个小对象肯定会被 10 个大对象要慢很多。



插一段 golang 垃圾回收的知识,golang 使用“三色标记法”作为垃圾回收算法,是“标记 - 清除法”的一个改进,相比“标记 - 清除法”优点在于它的标记(mark)的过程是并发的,不会 Stop The World。但缺点是对于巨量的小对象处理起来比较不擅长,有可能出现垃圾的产生速度比收集的速度还快的情况。gcMark 线程占用高很大几率就是对象产生速度大于垃圾回收速度了。



通过 profiling 定位 golang 性能问题 - 内存篇
通过 profiling 定位 golang 性能问题 - 内存篇
三色标记法



所以转换方向,又对内存做了一下 profiling:



go tool pprof http://your-prd-addr:8005/debug/pprof/heap
然后在浏览器里点击左上角 VIEW->flame graph,然后点击 SAMPLE->inuse_objects。



这样显示的是当前的对象数量:
https://aimuke.github.io/go/2020/07/06/go-pprof-case/
https://www.infoq.cn/article/f69uvzJUOmq276HBp1Qb
https://developer.aliyun.com/article/573743


Category golang