前言
今天来完成我们 pprof 工具实战的最后一个部分,小伙伴们跟着我一起来操作吧!
实战开始
先把炸弹程序运行起来再说
排查协程泄露
由于 GO 语言有垃圾回收 机制,所以一般不会出现内存泄露的情况,但是协程本身是有可能泄露的,也称协程失控,会导致内存泄露。
在浏览器打开 http://localhost:6060/debug/pprof/
我们可以看到协程有106条
106条虽然不多,但是对于这样一个小程序来说还是有点异常,所以抱着不怕一万就怕万一的心态,我们需要排查一下是否真的存在异常。
同样使用go tool pprof http://localhost:6060/debug/pprof/goroutine
来进入交互式的终端,然后使用top
和list
命令来排查问题
这次的问题比较隐晦,因为最顶上的调用不是自己写的函数,那么我们可以接着往下看,发现真正的问题所在是github.com/wolfogre/go-pprof-practice/animal/canidae/wolf.(*Wolf).Drink
方法。
接下来使用list
命令查看到底是什么问题
这里能看出来是创建了goroutine
并且睡眠30秒才退出,但是貌似没有太大的问题,为什么会有这么多协程呢,我们根据路径提示来到源文件查看代码。
Go
for i := 0; i < 10; i++ {
go func() {
time.Sleep(30 * time.Second)
}()
}
发现居然是在for
循环里面不停创建goroutine
,这样一来原因就排查出来了,接下来把它优化掉继续下一个实践。
排查锁的争用
排查到现在,我们已经解决了程序占用资源的问题,不过这并不是性能优化的全部,因为还有其他原因也会导致程序性能下降,相信大家很容易地就能想到不合理的锁的争用问题,那么接下来我们就需要排查锁的争用。在浏览器界面我们也发现存在锁的争用
话不多说,直接运行go tool pprof http://localhost:6060/debug/pprof/mutex
进入交互式终端,然后运行top
命令
这里发现问题是由github.com/wolfogre/go-pprof-practice/animal/canidae/wolf.(*Wolf).Howl
方法引起的,我们使用list
命令查看一下具体什么问题。
需要注意一个问题,不是被标记的锁就是有问题的,我们需要根据实际业务去判断,这里代码看不完整,所以我们需要查看完整的源代码。
Go
m := &sync.Mutex{}
m.Lock()
go func() {
time.Sleep(time.Second)
m.Unlock()
}()
m.Lock()
我们发现这里是由主协程上锁,然后在子协程里面做一些操作后释放,并且主协程在后面等待锁的释放,这在平时也许是正常的业务需要,但是子协程居然睡了一秒钟,并且没做什么有效的操作,所以应该优化掉。
排查阻塞操作
除了锁的争用会导致阻塞外,其他不合理的代码逻辑也是导致阻塞的原因之一,所以我们还要排查阻塞情况,在浏览器里面看到存在阻塞的情况。
运行go tool pprof http://localhost:6060/debug/pprof/block
进入交互式终端
运行top
命令
可以看到阻塞是由github.com/wolfogre/go-pprof-practice/animal/felidae/cat.(*Cat).Live
方法引起的
接下来使用list
命令探查实际代码
这里的情况和之前遇到的不一样,这里我们只看到了一堆方法调用,所以问题是在这一堆方法调用里面,那么到底是哪个有问题呢。结合之前top
命令的结果,我们断定是github.com/wolfogre/go-pprof-practice/animal/felidae/cat.(*Cat).Pee
方法有问题,所以输入list Pee
回车查看具体代码。
通过top
命令就能大概知道是因为channel
使用不当导致阻塞,这里就更直观的把代码展示了出来,是因为time.After
会阻塞当前线程一秒钟而不是睡眠一秒钟。嗯~有意思,优化掉。
总结
目前为止,本次pprof
实战就结束了,把常用的操作都做了一遍,不过pprof
还有很多需要我们去了解的地方,我们在以后学习的路上还要继续深入地学习。