文章目錄
- 分析準備工具
- go tool pprof參數分析
-
- 1、目前占用記憶體inuse_space
-
- 終端檢視
- web檢視
- 2、目前配置設定對象數量 inuse_objects
-
- 終端檢視
- web檢視
- 3、程式啟動到現在的記憶體使用 alloc_space
-
- 終端檢視
- web 檢視
- 4、從啟動到現在的總配置設定對象 alloc_objects
- 1、PProf
- 2、支援什麼使用模式
- 3、可以做什麼
- 4、 測試demo
- 5、 通路web
- 6、名額解析
-
- 1、runtime.futex
- 2、 runtime.gopark--協程名額
-
- gopark函數做的主要事情分為兩點:
- 調用過程
- 3、runtime.ready 喚起協程
- 4、runtime·notetslee 棧增長及執行時間檢測
- 7、進入中端
-
- 一 、檢視CPU資訊
-
- ==檢視記憶體配置設定取樣==
- ==檢視某個函數的細節==
- web
- 二、檢視阻塞堆棧資訊
-
- ==檢視某個函數細節==
- web
- 三、檢視協程堆棧資訊
-
- ==檢視某個函數細節==
- web檢視
- 四、檢視記憶體配置設定情況
-
- ==web檢視==
- 五、檢視互斥鎖資訊
-
- ==檢視某個函數細節==
- web檢視
- 六、檢視建立新OS線程的堆棧跟蹤
-
- ==檢視某個函數細節==
- web
- 8、 PProf 火焰圖
- 9、PProf 程式設計
-
- 終端檔案分析
-
- ==分析檔案和前邊一樣==
- web 分析
- 10、runtime.pprof程式設計
- 參考文獻
分析準備工具
brew install graphviz
go tool pprof參數分析
- -inuse_space 正在使用的記憶體
- -inuse_objects 正在使用的配置設定的的對象
- ** -alloc_space 從程式開始到現在總共配置設定的記憶體**
- ** -alloc_objects 從程式開始到現在總共配置設定的對象**
- **-total_delay **
- **-contentions **
- **-mean_delay **
1、目前占用記憶體inuse_space
終端檢視
go tool pprof -inuse_space http://1x.1xx.1x3.xx:4803/debug/pprof/heap
- flat:給定函數上運作耗時
- flat%:同上的 CPU 運作耗時總比例
- sum%:給定函數累積使用 CPU 總比例
- cum:目前函數加上它之上的調用運作總耗時
- cum%:同上的 CPU 運作耗時總比例
- 最後一列為函數名稱,在大多數的情況下,我們可以通過這五列得出一個應用程式的運作情況,加以優化
web檢視
go tool pprof -http=127.0.0.1:12345 -inuse_space http://1x.1xx.1x3.xx:4803/debug/pprof/heap
2、目前配置設定對象數量 inuse_objects
終端檢視
go tool pprof -inuse_objects http://1x.1xx.1xx.x0:487/sxx/debug/pprof/heap
web檢視
go tool pprof -http=127.0.0.1:12345 -inuse_objects http://1x.1xx.1xx.x0:487/sxx/debug/pprof/heap
3、程式啟動到現在的記憶體使用 alloc_space
終端檢視
- flat:給定函數上運作耗時
- flat%:同上的 CPU 運作耗時總比例
- sum%:給定函數累積使用 CPU 總比例
- cum:目前函數加上它之上的調用運作總耗時
- cum%:同上的 CPU 運作耗時總比例
- 最後一列為函數名稱,在大多數的情況下,我們可以通過這五列得出一個應用程式的運作情況,加以優化
web 檢視
4、從啟動到現在的總配置設定對象 alloc_objects
- flat:給定函數上運作耗時
- flat%:同上的 CPU 運作耗時總比例
- sum%:給定函數累積使用 CPU 總比例
- cum:目前函數加上它之上的調用運作總耗時
- cum%:同上的 CPU 運作耗時總比例
- 最後一列為函數名稱,在大多數的情況下,我們可以通過這五列得出一個應用程式的運作情況,加以優化
1、PProf
- runtime/pprof:采集程式(非 Server)的運作資料進行分析
- net/http/pprof:采集 HTTP Server 的運作時資料進行分析
2、支援什麼使用模式
- Report generation:報告生成
- Interactive terminal use:互動式終端使用
- Web interface:Web 界面
3、可以做什麼
- CPU Profiling:CPU 分析,按照一定的頻率采集所監聽的應用程式 CPU(含寄存器)的使用情況,可确定應用程式在主動消耗 CPU 周期時花費時間的位置
- Memory Profiling:記憶體分析,在應用程式進行堆配置設定時記錄堆棧跟蹤,用于監視目前和曆史記憶體使用情況,以及檢查記憶體洩漏
- Block Profiling:阻塞分析,記錄 goroutine 阻塞等待同步(包括定時器通道)的位置
- Mutex Profiling:互斥鎖分析,報告互斥鎖的競争情況
4、 測試demo
% tree
.
├── data
│ └── d.go
├── go.mod
└── main.go
1 directory, 3 files
Main.go
package main
import (
"log"
"net/http"
_ "net/http/pprof"
"pproftest/data"
"time"
)
func main() {
go func() {
for {
time.Sleep(1*time.Second)
log.Println(data.Add("https://github.com/EDDYCJY"))
}
}()
http.ListenAndServe("0.0.0.0:6060", nil)
}
d.go
package data
var datas []string
func Add(str string) string {
data := []byte(str)
sData := string(data)
datas = append(datas, sData)
return sData
}
啟動程式
go run main.go
實際應用
package main
import (
"fmt"
"net/http"
_ "net/http/pprof" // 第一步~
)
// 一段有問題的代碼
func do() {
var c chan int
for {
select {
case v := <-c:
fmt.Printf("我是有問題的那一行,因為收不到值:%v", v)
default:
}
}
}
func main() {
// 執行一段有問題的代碼
for i := 0; i < 4; i++ {
go do()
}
http.ListenAndServe("0.0.0.0:6061", nil)
}
5、 通路web
http://127.0.0.1:6060/debug/pprof/
描述
類型 | 描述 |
---|---|
allocs | 記憶體配置設定情況的采樣資訊 |
blocks | 阻塞操作情況的采樣資訊 |
cmdline | 顯示程式啟動指令參數及其參數 |
goroutine | 顯示目前所有協程的堆棧資訊 |
heap | 堆上的記憶體配置設定情況的采樣資訊 |
mutex | 鎖競争情況的采樣資訊 |
profile | cpu占用情況的采樣資訊,點選會下載下傳檔案 |
threadcreate | 系統線程建立情況的采樣資訊 |
trace | 程式運作跟蹤資訊 |
6、名額解析
1、runtime.futex
CPU的名額,通常這個和鎖有關系
一般情況是GC的時候進行的STW的開啟鎖定鎖導緻
2、 runtime.gopark–協程名額
gopark函數在協程的實作上扮演着非常重要的角色,用于協程的切換,協程切換的原因一般有以下幾種情況:
- 系統調用或者網絡調用;
- channel讀寫條件不滿足;
- 搶占式排程時間片結束;
gopark函數做的主要事情分為兩點:
- 解除目前goroutine的m的綁定關系,将目前goroutine狀态機切換為等待狀态;
- 調用一次schedule()函數,在局部排程器P發起一輪新的排程。
調用過程
func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason waitReason, traceEv byte, traceskip int) {
if reason != waitReasonSleep {
checkTimeouts() // timeouts may expire while two goroutines keep the scheduler busy
}
mp := acquirem()
gp := mp.curg
status := readgstatus(gp)
if status != _Grunning && status != _Gscanrunning {
throw("gopark: bad g status")
}
mp.waitlock = lock
mp.waitunlockf = *(*unsafe.Pointer)(unsafe.Pointer(&unlockf))
gp.waitreason = reason
mp.waittraceev = traceEv
mp.waittraceskip = traceskip
releasem(mp)
// can't do anything that might move the G between Ms here.
mcall(park_m)
}
源碼裡面最重要的一行就是調用
mcall(park_m)
函數,park_m是一個函數指針。mcall在golang需要進行協程切換時被調用,做的主要工作是:
- 切換目前線程的堆棧從g的堆棧切換到g0的堆棧;
- 并在g0的堆棧上執行新的函數fn(g);
- 儲存目前協程的資訊( PC/SP存儲到g->sched),當後續對目前協程調用goready函數時候能夠恢複現場;
3、runtime.ready 喚起協程
func goready(gp *g, traceskip int) {
// 切換到g0的棧
systemstack(func() {
ready(gp, traceskip, true)
})
}
goready函數相比gopark函數來說簡單一些,主要功能就是喚醒某一個goroutine,該協程轉換到runnable的狀态,并将其放入P的local queue,等待排程。
4、runtime·notetslee 棧增長及執行時間檢測
same as runtime·notetsleep, but called on user g (not g0)
// calls only nosplit functions between entersyscallblock/exitsyscall
檢測棧增長及監控G的執行時間是否超過10ms,如果超過将目前G和M綁定,解綁P
7、進入中端
一 、檢視CPU資訊
cpu(CPU Profiling): $HOST/debug/pprof/profile,預設進行 30s 的 CPU Profiling,得到一個分析用的 profile 檔案
另外啟動中端,等待30s
go tool pprof http://localhost:6060/debug/pprof/profile\?seconds\=60
類型 | 描述 | 舉例 |
---|---|---|
flat | 該函數占用CPU的耗時 | selectnbrecv占用CPU的耗時是12.29s |
flat% | 該函數占用CPU的耗時的百分比 | selectnbrecv耗時:12.29s,cpu總耗時:29.14,12.29/29.14=42.18 |
sum% | top指令中排在它上面的函數以及本函數flat%之和 | chanrecv:42.18%+30.47% = 72.65% |
cum | 目前函數加上該函數調用之前的累計CPU耗時 | chanrecv:8.88+0.54=9.42 |
cum% | 目前函數加上該函數調用之前的累計CPU耗時的百分比 | 9.42/29.14=32.33% |
最後一列 | 目前函數名稱 | - |
檢視記憶體配置設定取樣
預設情況下取樣時隻取目前記憶體使用情況,可以加可選指令alloc_objects,将從程式開始時的記憶體取樣
go tool pprof -alloc_objects -http=127.0.0.1:12345 http://xxx:9999/debug/pprof/heap
檢視某個函數的細節
終端模式下輸入
list 加函數名
- flat:給定函數上運作耗時
- flat%:同上的 CPU 運作耗時總比例
- sum%:給定函數累積使用 CPU 總比例
- cum:目前函數加上它之上的調用運作總耗時
- cum%:同上的 CPU 運作耗時總比例
- 最後一列為函數名稱,在大多數的情況下,我們可以通過這五列得出一個應用程式的運作情況,加以優化
web
go tool pprof -http=127.0.0.1:1234 http://localhost:6061/debug/pprof/profile\?seconds\=10
線越粗越有問題,耗時越高
終端模式下
web png 或者pdf
檢視do函數
list main.do
發現有問題的行數在文中具體的位置,原來是卡住了,加上default休眠n秒即可解決。
二、檢視阻塞堆棧資訊
block(Block Profiling):$HOST/debug/pprof/block,檢視導緻阻塞同步的堆棧跟蹤
go tool pprof http://localhost:6061/debug/pprof/block\?seconds\=10
- flat:給定函數上運作耗時
- flat%:同上的 CPU 運作耗時總比例
- sum%:給定函數累積使用 CPU 總比例
- cum:目前函數加上它之上的調用運作總耗時
- cum%:同上的 CPU 運作耗時總比例
- 最後一列為函數名稱,在大多數的情況下,我們可以通過這五列得出一個應用程式的運作情況,加以優化
檢視某個函數細節
和上邊CPu的一樣list
web
Web 也是一樣
三、檢視協程堆棧資訊
goroutine:$HOST/debug/pprof/goroutine,檢視目前所有運作的 goroutines 堆棧跟蹤
go tool pprof http://localhost:6061/debug/pprof/goroutine\?seconds\=10
- flat:給定函數上運作耗時
- flat%:同上的 CPU 運作耗時總比例
- sum%:給定函數累積使用 CPU 總比例
- cum:目前函數加上它之上的調用運作總耗時
- cum%:同上的 CPU 運作耗時總比例
- 最後一列為函數名稱,在大多數的情況下,我們可以通過這五列得出一個應用程式的運作情況,加以優化
檢視某個函數細節
- flat:給定函數上運作耗時
- flat%:同上的 CPU 運作耗時總比例
- sum%:給定函數累積使用 CPU 總比例
- cum:目前函數加上它之上的調用運作總耗時
- cum%:同上的 CPU 運作耗時總比例
web檢視
go tool pprof -http=127.0.0.1:1345 http://localhost:6061/debug/pprof/goroutine
四、檢視記憶體配置設定情況
- -inuse_space:分析應用程式的常駐記憶體占用情況
- -alloc_objects:分析應用程式的記憶體臨時配置設定情況
heap(Memory Profiling): $HOST/debug/pprof/heap,檢視活動對象的記憶體配置設定情況
go tool pprof http://localhost:6061/debug/pprof/heap
- flat:給定函數上運作耗時
- flat%:同上的 CPU 運作耗時總比例
- sum%:給定函數累積使用 CPU 總比例
- cum:目前函數加上它之上的調用運作總耗時
- cum%:同上的 CPU 運作耗時總比例
- 最後一列為函數名稱,在大多數的情況下,我們可以通過這五列得出一個應用程式的運作情況,加以優化
web檢視
go tool pprof -http=127.0.0.1:1345 http://localhost:6061/debug/pprof/heap
五、檢視互斥鎖資訊
mutex(Mutex Profiling):$HOST/debug/pprof/mutex,檢視導緻互斥鎖的競争持有者的堆棧跟蹤
go tool pprof http://localhost:6061/debug/pprof/mutex
檢視某個函數細節
同上
web檢視
go tool pprof -http=127.0.0.1:1345 http://localhost:6061/debug/pprof/mutex
六、檢視建立新OS線程的堆棧跟蹤
threadcreate:$HOST/debug/pprof/threadcreate,檢視建立新OS線程的堆棧跟蹤
go tool pprof http://localhost:6061/debug/pprof/threadcreate
- flat:給定函數上運作耗時
- flat%:同上的 CPU 運作耗時總比例
- sum%:給定函數累積使用 CPU 總比例
- cum:目前函數加上它之上的調用運作總耗時
- cum%:同上的 CPU 運作耗時總比例
- 最後一列為函數名稱,在大多數的情況下,我們可以通過這五列得出一個應用程式的運作情況,加以優化
檢視某個函數細節
list runtime.main
web
go tool pprof -http=127.0.0.1:1345 http://localhost:6061/debug/pprof/threadcreate
8、 PProf 火焰圖
每一塊代表一個函數,越大代表占用 CPU 的時間更長
另一種可視化資料的方法是火焰圖,需手動安裝原生 PProf 工具:
(1) 安裝 PProf
$ go get -u github.com/google/pprof
(2) 啟動 PProf 可視化界面:
$ pprof -http=:8080 cpu.prof
9、PProf 程式設計
終端檔案分析
代碼
package main
import (
"fmt"
"log"
"os"
"runtime/pprof"
"time"
)
func do() {
var c chan int
for {
select {
case v := <-c:
fmt.Println("有問題", v)
default:
fmt.Println("default")
}
}
}
func main() {
var (
file *os.File
err error
)
if file, err = os.Create("./cpu.prof"); nil != err {
log.Fatal(err)
}
//1、擷取CPU資訊
if err = pprof.StartCPUProfile(file); err != nil {
log.Fatal(err)
}
defer pprof.StopCPUProfile()
for i := 0; i < 4; i++ {
go do()
}
time.Sleep(10 * time.Second)
}
生成
分析檔案和前邊一樣
go tool pprof <binary> <source>
binary:代表二進制檔案路徑。
source:代表生成的分析資料來源,可以是本地檔案(前文生成的cpu.prof),也可以是http位址(比如:go tool pprof http://127.0.0.1:6060/debug/pprof/profile)
go tool pprof cpu.prof
web 分析
package main
import (
"fmt"
"net/http"
_ "net/http/pprof" // 第一步~
)
// 一段有問題的代碼
func do() {
var c chan int
for {
select {
case v := <-c:
fmt.Printf("我是有問題的那一行,因為收不到值:%v", v)
default:
}
}
}
func main() {
// 執行一段有問題的代碼
for i := 0; i < 4; i++ {
go do()
}
http.ListenAndServe("0.0.0.0:6061", nil)
}
10、runtime.pprof程式設計
擷取CPU資訊和Heap資訊
package main
import (
"fmt"
"log"
"os"
"runtime/pprof"
"time"
)
func do() {
var c chan int
for {
select {
case v := <-c:
fmt.Println("有問題", v)
default:
fmt.Println("default")
}
}
}
func main() {
var (
file, file1 *os.File
err error
)
if file, err = os.Create("./cpu.prof"); nil != err {
log.Fatal(err)
}
if file1, err = os.Create("./heap.prof"); nil != err {
log.Fatal(err)
}
//1、擷取CPU資訊
if err = pprof.StartCPUProfile(file); err != nil {
log.Fatal(err)
}
defer pprof.StopCPUProfile()
//2、擷取heap資訊
if err = pprof.WriteHeapProfile(file1); nil != err {
log.Fatal(err)
}
for i := 0; i < 4; i++ {
go do()
}
time.Sleep(10 * time.Second)
}
參考文獻
https://segmentfault.com/a/1190000016412013
https://segmentfault.com/a/1190000016354758