前言
上期我们简单做了一个排查CPU占用过高的实践,相信小伙伴们已经熟悉了go tool pprof
这个工具了,这期我们实验的节奏会更快,要做好几个实验哦
实战开始
老规矩,先把 炸弹程序 运行起来,然后打开任务管理器观察有哪些异常的性能数据。
排查内存占用过高
我们很明显地看到这个程序的占用了 7GB 多的内存,实在是太恐怖啦。
我们要排查的是内存占用过高,理所当然的应该借助 go tool pprof
命令来分析 heap 文件。接下来,准备动手,打开命令行输入go tool pprof http://localhost:6060/debug/pprof/heap
。很快啊,熟悉的交互式终端就出现了。
首先使用top
命令查看到底是哪些方法占用了这么多内存
经过一番仔细的查看,发现原来是
github.com/wolfogre/go-pprof-practice/animal/muridae/mouse.(*Mouse).Pee.func1
方法和github.com/wolfogre/go-pprof-practice/animal/muridae/mouse.(*Mouse).Steal
方法各自占用了 1.2GB 的内存。
有两处地方都有问题,我们先使用list
命令查看Pee
方法有什么问题
原来是在Pee
方法里面启动了一个goroutine
来不断地往一个名叫slowBuffer
的二维切片里面追加一维切片。好了,问题找到了,接下来使用list
命令查看Steal
方法有什么问题。
定睛一看,原来是一样的问题,那么我们先把这两个地方 优化(注释) 掉。
此处省略优化的详细步骤...
在优化后重新启动程序,可以发现 CPU 和 内存 看起来都很正常,没有出现异常的数据,那么问题真的就没有了吗?
排查频繁GC
GC 的全称是 Garbage Collection,翻译过来为垃圾回收,用在计算机领域就被称为内存回收。这是一种自动内存管理的机制,它会自动回收那些被分配但不再被使用的内存。这种机制可以帮助开发者避免很多常见的内存管理错误,例如内存泄漏和野指针等问题。
大家都知道golang
是追求高性能的,那么频繁的 GC 肯定会影响程序的性能,这是golang
所不允许的。虽然现在 CPU 和 内存 的使用率都没有问题,但是我们还是要排查 GC 的回收情况。
所以我们接下来的排查不是排查哪里占用了大量的内存,而是排查哪里在频繁地申请内存又不用,触发GC,影响程序性能。
正常情况来说,我们是不知道 GC 的情况的,那么我们如何来了解 GC 是否频繁发生呢?答案就是查看 GC 日志。为了能查看 GC 的日志,我们需要设置环境变量set GODEBUG=gctrace=1
,这样设置的环境变量只在当前终端窗口有效,能有效防止影响到其他 GO 程序的输出。
接下来在终端输入go-pprof-practice.exe | findstr gc
,运行 GO 程序的同时把日志做一次过滤,能有效屏蔽其他日志对我们的干扰。
通过 GC 日志我们发现 GC 差不多每3秒就要发生一次,每次都是申请了16MB 的空间然后被 GC 回收掉了,所以大概率是某个地方申请了16MB 空间然后没有使用,被 GC 回收掉了。
对于问题有了自己初步的判断,接下来就是使用go tool pprof
命令来检查实际的问题了。还是原来的步骤,使用top list
命令来排查相关的问题,不过这里有个前提,就是需要等待程序运行一会,因为内存的分配和回收的频率都是需要一段时间来统计的。
等待程序运行一段时间后输入go tool pprof http://localhost:6060/debug/pprof/allocs
进入交互式终端,然后输入top
命令查看内存分配情况
可以看到github.com/wolfogre/go-pprof-practice/animal/canidae/dog.(*Dog).Run
方法存在问题,接下来使用list
命令查看有问题的代码具体在哪。
我们可以看到在Run
方法里面存在这样一行代码_ = make([]byte, 16*constant.Mi)
,这行代码分配了16MB 内存,但是使用了匿名变量 来忽略返回值,导致这个内存没有被使用,没在使用的空间自然就需要 GC 来回收了。
老规矩,发现问题后注释掉(练习需要,切勿模仿 )
重新启动后观察一下 GC 日志。很好,没有问题了。
总结
目前用到的命令其实很少也很简单,只需要我们有一双善于发现问题的眼睛,就能使用工具快速定位问题。
好了,还剩几个实践操作我们下期见!