这是我参与「第三届青训营 -后端场」笔记创作活动的的第5篇笔记, 第三次课主要讲了GO语言的高质量变成和性能调优实战,本文是针对性能分析工具PProf部分做的笔记
一、功能简介
二、排查实战
1. 搭建pprof实践项目
项目中埋了一些代码炸弹,可以使用pprof排查
2.浏览器查看指标 (http://localhost:6060/debug/pprof/)
- allocs:内存分配情况
- blocks:阻塞撮作情况
- cmdline:程序启动命令及
- goroutine:当前所有goroutine的堆找信息
- heap:堆上内存使用情况(同alloc)
- mutex:锁竞争操作情况
- profile: CPU占用情况
- threadcreate:当前所有创建的系统线程的堆栈信息
- trace:程序运行跟踪信息
1. CPU
- 下载 graphviz 同时配置环境变量
- 运行实战程序
- 执行命令go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/profile?seconds=10"
- 浏览器界面
上述分别是graph,top表格和Source的形式,可以看出占用cpu最多的代码都指向了 tiger.Eat, 检查源代码可以看到确实有问题
func (t *Tiger) Eat() {
log.Println(t.Name(), "eat")
loop := 10000000000
for i := 0; i < loop; i++ {
// do nothing
}
}
2. Heap堆内存
- go tool pprof -http=:8081 "http://localhost:6060/debug/pprof/heap"
- 图
- top表格
- source
可以看出,占用堆内存最高的代码都指向了mouse.Steal函数, 查看源码如下
const (
Ki = 1024
Mi = Ki * Ki
Gi = Ki * Mi
Ti = Ki * Gi
Pi = Ki * Ti
)
func (m *Mouse) Steal() {
log.Println(m.Name(), "steal")
max := constant.Gi
for len(m.buffer) * constant.Mi < max {
m.buffer = append(m.buffer, [constant.Mi]byte{})
}
}
3. goroutine协程
- goroutine协程泄露也会导致内存泄漏
- go tool pprof -http=:8082 "http://localhost:6060/debug/pprof/goroutine"
- graph
- 火焰图
- 从上到下表示调用顺序,每一块代表一个函数,越长代表占用cpu时间更长
- 火焰图是动态的,支持点击块进行分析
- Source视图
- 源码
func (w *Wolf) Drink() {
log.Println(w.Name(), "drink")
for i := 0; i < 10; i++ {
go func() {
time.Sleep(30 * time.Second)
}()
}
}
4. mutex锁
- go tool pprof -http=:8082 "http://localhost:6060/debug/pprof/mutex"
- graph
- Source
- 发现存在一个锁操作,打开source观察, 发现这里sleep了1s才解锁,这个不是业务需要
func (w *Wolf) Howl() {
log.Println(w.Name(), "howl")
m := &sync.Mutex{}
m.Lock()
go func() {
time.Sleep(time.Second)
m.Unlock()
}()
m.Lock()
}
5. block阻塞
- go tool pprof -http=:8082 "http://localhost:6060/debug/pprof/block"
- graph
- source
- Cat.pee()函数 读取了一个time.After()生成的Channel,这就导致了这个协程阻塞了1s,而不是等待了1s