如何使用 pprof 來定位現(xiàn)網(wǎng) Golang 問題,已經(jīng)是一名 Gopher 所需要掌握的必備技能了。我們在實(shí)際工作中也經(jīng)常使用它來定位現(xiàn)網(wǎng)問題。網(wǎng)上有很多文章來描述 pprof 的使用,但是實(shí)際的線上使用場景,卻和各個(gè)文章的描述的多少有些差異。 比如網(wǎng)上大部分文章都會(huì)告訴你,使用命令行打開 we
如何使用 pprof 來定位現(xiàn)網(wǎng) Golang 問題,已經(jīng)是一名 Gopher 所需要掌握的必備技能了。我們在實(shí)際工作中也經(jīng)常使用它來定位現(xiàn)網(wǎng)問題。網(wǎng)上有很多文章來描述 pprof 的使用,但是實(shí)際的線上使用場景,卻和各個(gè)文章的描述的多少有些差異。
比如網(wǎng)上大部分文章都會(huì)告訴你,使用命令行打開 web 端口,在瀏覽器打開 web 界面,接著告訴你如何在 web 界面查看整個(gè)服務(wù)情況。但實(shí)際情況是,我們的線上服務(wù)基本都是跑在 centos 操作系統(tǒng)中的,由于安全問題,對外,對辦公網(wǎng)絡(luò)不可能開放web 訪問端口,更無從通過 web 界面查看服務(wù)情況,故此,web 界面在現(xiàn)網(wǎng)基本不可用。
網(wǎng)上還有一些文章告訴你,如何使用 go tool pprof 命令來交互式定位問題。但這里隱藏的前提就是需要安裝 go 工具。但于我來說,這個(gè)是極其不方便的,我們的現(xiàn)網(wǎng)服務(wù)是使用鏡像安裝,而鏡像中只會(huì)安裝編譯好的業(yè)務(wù)二進(jìn)制文件,并不會(huì)安裝 go 工具。除非我們使用運(yùn)維權(quán)限來安裝 go 命令。故此,我希望知道在無 go 工具的時(shí)候我能做些什么。
我實(shí)際使用的現(xiàn)網(wǎng)環(huán)境是什么樣的呢?- k8s 上運(yùn)行無狀態(tài)的 centos7 操作系統(tǒng)的鏡像,鏡像中打包了所需要的二進(jìn)制命令,除此之外,只安裝了基礎(chǔ)的 vi,curl 命令。
那么我尋求一種能不需要安裝,很快分析出 Golang 性能問題的辦法,故有此紀(jì)錄。
首先,first of all,巧婦難為無米之炊,這個(gè)米就是你在服務(wù)中開啟了一個(gè) pprof 端口。我們的服務(wù)都是 web 類型的服務(wù),提供 http,只需要引入一行代碼即可。
import _ "net/http/pprof"
如果不是 web 服務(wù)的話,則除了引入上面的庫之外,還需要開啟一個(gè) goroutine 來監(jiān)聽服務(wù)。
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
如此,在你的服務(wù)運(yùn)行過程中,就能通過本地的 6060 端口來進(jìn)行數(shù)據(jù)獲取。有米之后,才能烹煮大餐。
pprof 是一個(gè)套餐,里面有不少餐食,而我們接下來要做的,則需要分析我們想定位的問題,比如我們想看下如下幾個(gè)問題:
以上每個(gè)問題,我們都需要使用 pprof 套件中對應(yīng)的工具來解決。
我們最常犯的問題是 goroutine 泄漏。在 Golang 里面,開啟一個(gè)協(xié)程是這么容易,導(dǎo)致我們在很多地方會(huì) go 一個(gè)函數(shù)出去,但是是否這些函數(shù)都按照預(yù)期回收了呢?我們這里就需要用到 pprof 里面的 goroutine 子命令來進(jìn)行分析了。
在 go 中,分析 goroutine,我們并不需要安裝 go tool 工具,pprof 端口提供我們直接使用 curl 命令進(jìn)行定位的能力。這也是我最常用的方法。
首先我們需要明確,goroutine 的分布是一個(gè)狀態(tài)值,是可以直接打印出來的,當(dāng)前進(jìn)程中有多少 goroutine,分別是由哪些函數(shù)來創(chuàng)建的。并不需要進(jìn)行時(shí)間段采樣。
curl -o goroutine.log localhost:6060/debug/pprof/goroutine?debug=1
這里的 debug=1 是用來表示輸出的格式,它的值分別有 1/2和不填。
當(dāng)填寫debug=1 的時(shí)候,輸出的是可讀性文本,是一種按照函數(shù)棧聚類分析的結(jié)果文本:
goroutine profile: total 2065
933 @ 0x4492d6 0x415e4c 0x4158b8 0x153ddf1 0x159a61e 0x47aa41
# 0x153ddf0 github.com/Shopify/sarama.(*partitionProducer).dispatch+0x1b0 /root/go/pkg/mod/github.com/!shopify/[email protected]/async_producer.go:545
# 0x159a61d github.com/Shopify/sarama.withRecover+0x3d /root/go/pkg/mod/github.com/!shopify/[email protected]/utils.go:43
194 @ 0x4492d6 0x415e4c 0x4158b8 0x153f385 0x159a61e 0x47aa41
# 0x153f384 github.com/Shopify/sarama.(*asyncProducer).newBrokerProducer.func1+0x64 /root/go/pkg/mod/github.com/!shopify/[email protected]/async_producer.go:694
# 0x159a61d github.com/Shopify/sarama.withRecover+0x3d /root/go/pkg/mod/github.com/!shopify/[email protected]/utils.go:43
如以上文本,說明了當(dāng)前進(jìn)程總共啟動(dòng)了 2065 個(gè) goroutine,其中列出了排名靠前的 2 個(gè)堆棧:
github.com/Shopify/sarama.(*partitionProducer).dispatch
創(chuàng)建了 933 個(gè) goroutine,在預(yù)期內(nèi)
github.com/Shopify/sarama.(*asyncProducer).newBrokerProducer.func1
創(chuàng)建了 194 個(gè) goroutine,在預(yù)期內(nèi)
這樣分析前幾個(gè)占用 goroutine 數(shù)最多的堆棧,就大概能知道業(yè)務(wù)進(jìn)程是否有 goroutine 泄漏了。debug=1 也是我最常用的分析方式。
填寫 debug=2 的時(shí)候,輸出的也是可讀性文本,但它的輸出是按照 goroutine 分類的結(jié)果文本:
goroutine 34890598 [running]:
runtime/pprof.writeGoroutineStacks({0x2917520, 0xc002716c40})
/usr/go/src/runtime/pprof/pprof.go:693 +0x70
runtime/pprof.writeGoroutine({0x2917520, 0xc002716c40}, 0x0)
/usr/go/src/runtime/pprof/pprof.go:682 +0x2b
runtime/pprof.(*Profile).WriteTo(0x21ae7e0, {0x2917520, 0xc002716c40}, 0xc)
/usr/go/src/runtime/pprof/pprof.go:331 +0x14b
net/http/pprof.handler.ServeHTTP({0xc00b075ea1, 0xc000153000}, {0x2939d10, 0xc002716c40}, 0xc00b075e94)
/usr/go/src/net/http/pprof/pprof.go:253 +0x49a
net/http/pprof.Index({0x2939d10, 0xc002716c40}, 0xc007f8ce00)
/usr/go/src/net/http/pprof/pprof.go:371 +0x12e
net/http.HandlerFunc.ServeHTTP(0xc00428aa30, {0x2939d10, 0xc002716c40}, 0xc00b075eab)
/usr/go/src/net/http/server.go:2047 +0x2f
...
goroutine 1 [select, 6426 minutes]:
git.code.oa.com/my-go/server.(*Server).Serve(0xc000014cb0)
/root/go/pkg/mod/[email protected]/server/serve_unix.go:46 +0x3ae
main.main()
/data/__qci/root-workspaces/__qci-pipeline-251750-1/main.go:111 +0x4d6
goroutine 19 [syscall, 702 minutes]:
syscall.Syscall6(0xe8, 0x7, 0xc00020fbec, 0x7, 0xffffffffffffffff, 0x0, 0x0)
/usr/go/src/syscall/asm_linux_amd64.s:43 +0x5
golang.org/x/sys/unix.EpollWait(0x2, {0xc00020fbec, 0x2, 0xc0091c98c0}, 0xc00020fcb4)
/root/go/pkg/mod/golang.org/x/[email protected]/unix/zsyscall_linux_amd64.go:56 +0x58
github.com/fsnotify/fsnotify.(*fdPoller).wait(0xc000122300)
/root/go/pkg/mod/github.com/fsnotify/[email protected]/inotify_poller.go:86 +0x7d
github.com/fsnotify/fsnotify.(*Watcher).readEvents(0xc00010e690)
/root/go/pkg/mod/github.com/fsnotify/[email protected]/inotify.go:192 +0x2b0
created by github.com/fsnotify/fsnotify.NewWatcher
/root/go/pkg/mod/github.com/fsnotify/[email protected]/inotify.go:59 +0x1c7
這個(gè)文本非常長,它把每個(gè) goroutine 的堆棧信息,運(yùn)行狀態(tài),運(yùn)行時(shí)長都列了出來。如上圖,列了 3 個(gè) gorouine ,
debug=2 的方式最有用的就是 goroutine 的運(yùn)行時(shí)長和更詳細(xì)的堆棧信息,在使用 debug=1 定位出可疑堆棧的時(shí)候,用堆棧的函數(shù)去 debug=2 中找,能大概看出來它已經(jīng)啟動(dòng)多久,更具體的堆棧有哪些,當(dāng)前是卡在哪個(gè)系統(tǒng)函數(shù)中,能更快定位問題。
這兩種 debug 的值是可以直接在控制臺(tái)通過 curl 和 vi進(jìn)行查看的,對于現(xiàn)網(wǎng)非常方便的。如果不指定 debug ,輸出的 goroutine.log 打開就是一種二進(jìn)制亂碼,它就只能通過 go tool pprof 工具打開,這樣在沒有g(shù)o tool 的環(huán)境中,就需要想辦法安裝 go 工具,或者下載到有 go 工具的環(huán)境才能閱讀了。當(dāng)然使用 go tool 工具很麻煩,但是它能看出更多的信息。
curl -o goroutine.log localhost:11014/debug/pprof/goroutine
go tool pprof goroutine.log
進(jìn)入控制臺(tái),我們最常使用的就是
top [100]
命令,比如
top 100
(pprof) top100
Showing nodes accounting for 2027, 99.75% of 2032 total
Dropped 204 nodes (cum <= 10)
flat flat% sum% cum cum%
2027 99.75% 99.75% 2027 99.75% runtime.gopark
0 0% 99.75% 186 9.15% bufio.(*Reader).Peek
0 0% 99.75% 19 0.94% bufio.(*Reader).Read
0 0% 99.75% 187 9.20% bufio.(*Reader).fill
0 0% 99.75% 32 1.57% my-go/kafka.(*singleConsumerHandler).ConsumeClaim
0 0% 99.75% 16 0.79% my-go/transport.(*serverTransport).servePacket
0 0% 99.75% 16 0.79% my-go/transport.(*serverTransport).serveUDP
0 0% 99.75% 16 0.79% github.com/Shopify/sarama.(*Broker).Fetch
0 0% 99.75% 16 0.79% github.com/Shopify/sarama.(*Broker).readFull
0 0% 99.75% 62 3.05% github.com/Shopify/sarama.(*Broker).responseReceiver
0 0% 99.75% 16 0.79% github.com/Shopify/sarama.(*Broker).sendAndReceive
0 0% 99.75% 168 8.27% github.com/Shopify/sarama.(*asyncProducer).newBrokerProducer.func1
0 0% 99.75% 16 0.79% github.com/Shopify/sarama.(*brokerConsumer).fetchNewMessages
0 0% 99.75% 17 0.84% github.com/Shopify/sarama.(*brokerConsumer).subscriptionConsumer
0 0% 99.75% 17 0.84% github.com/Shopify/sarama.(*brokerConsumer).subscriptionManager
0 0% 99.75% 168 8.27% github.com/Shopify/sarama.(*brokerProducer).run
0 0% 99.75% 16 0.79% github.com/Shopify/sarama.(*bufConn).Read
0 0% 99.75% 32 1.57% github.com/Shopify/sarama.(*consumerGroupSession).consume
0 0% 99.75% 33 1.62% github.com/Shopify/sarama.(*consumerGroupSession).consume.func1
0 0% 99.75% 33 1.62% github.com/Shopify/sarama.(*consumerGroupSession).consume.func2
0 0% 99.75% 33 1.62% github.com/Shopify/sarama.(*partitionConsumer).dispatcher
0 0% 99.75% 33 1.62% github.com/Shopify/sarama.(*partitionConsumer).responseFeeder
0 0% 99.75% 933 45.92% github.com/Shopify/sarama.(*partitionProducer).dispatch
0 0% 99.75% 33 1.62% github.com/Shopify/sarama.newConsumerGroupClaim.func1
0 0% 99.75% 33 1.62% github.com/Shopify/sarama.newConsumerGroupSession.func1
0 0% 99.75% 32 1.57% github.com/Shopify/sarama.newConsumerGroupSession.func2
0 0% 99.75% 1453 71.51% github.com/Shopify/sarama.withRecover
這個(gè) top 命令其實(shí)帶的信息就值得好好琢磨琢磨了。
Showing nodes accounting for 2027, 99.75% of 2032 total
這里的 top 100 已經(jīng)展示了所有 2032 個(gè) goroutine 中的 2027 個(gè)了,占比 99.75%。所以通過這個(gè),我們就能知道,top 100 就已經(jīng)很夠分析了,可以不同再擴(kuò)大 top 的個(gè)數(shù)了。
但是即使是 top100,我們?nèi)空故疽蔡罅,所以我們需要舍棄一些函?shù),
Dropped 204 nodes (cum <= 10)
告訴我們,以下列表舍棄了 cum <= 10 的函數(shù),具體 cum 值是什么意思呢,下面一起說。
flat, sum, cum 這三個(gè)值在 pprof 的各個(gè)工具中都會(huì)出現(xiàn),要知道他們的意思才能進(jìn)一步分析:
上栗子:我現(xiàn)在有個(gè)調(diào)用鏈,funcA -> funcB -> funcC。且在 funcA/B/C 中,除了函數(shù)調(diào)用之外,他們函數(shù)自身也會(huì)各自創(chuàng)建 goroutine,我們假設(shè) funcA/B/C 各自函數(shù)創(chuàng)建的 goroutine 數(shù)為 1/2/3。
那么對于 funcB 而言,它的 flat 指的是 funcB 這個(gè)函數(shù)自己創(chuàng)建的數(shù)量,為 2。而 sum 表示的是 funB 這個(gè)函數(shù),及調(diào)用它的函數(shù) funcA 共同創(chuàng)建的 goroutine 的數(shù)量,為 1+2 =3。而 cum 表示的是 funB 和它調(diào)用的函數(shù) funcC 創(chuàng)建的 goroutine 的數(shù)量,為 2+3 = 5。
而每個(gè)函數(shù)的這三個(gè)指標(biāo)在所有函數(shù)中的占比就為三個(gè)指標(biāo)對應(yīng)的百分比:flat% sum% cum%。
理解了這些,我們就能理解為什么在第一列的gopark 是那樣的數(shù)值了。
flat flat% sum% cum cum%
2027 99.75% 99.75% 2027 99.75% runtime.gopark
這是一個(gè)最底層函數(shù),基本所有的 goroutine 創(chuàng)建之后都會(huì)調(diào)用它,所以它自身創(chuàng)建的 goroutine 數(shù)為 flat: 2027,且它沒有下層調(diào)用,所以它的cum 也為 2027。
再看一個(gè)第二列的數(shù)值:
flat flat% sum% cum cum%
0 0% 99.75% 186 9.15% bufio.(*Reader).Peek
bufio.(*Reader).Peek
這個(gè)函數(shù)自身不創(chuàng)建 goroutine(flat = 0),但是調(diào)用它的函數(shù)創(chuàng)建的 goroutine 占比 ( 99.75% ),它的下層調(diào)用創(chuàng)建了 186 個(gè) goroutine,占比 9.15%。
所以我們要分析 goroutine 泄漏的時(shí)候要看哪個(gè)值呢?- 對的,cum。
找到 cum 占比最大的函數(shù),這里一般我們都會(huì)查找非系統(tǒng)調(diào)用函數(shù),(因?yàn)橄到y(tǒng)調(diào)用函數(shù)不大可能有問題),那么我們就知道我們的哪個(gè)應(yīng)用函數(shù)存在 goroutine 泄漏。
這里另外說下,有的文章說可以下一步用 list + 函數(shù)名 的方式來列出最占用資源的行數(shù)。行,當(dāng)然行,但是它的使用情景更為苛刻了 - 需要在現(xiàn)網(wǎng)機(jī)器安裝源碼+依賴庫。而這個(gè)在現(xiàn)網(wǎng)環(huán)境基本是不可能的,所以這個(gè)list 命令在實(shí)際分析中我自己是很少用到的。
如果我們是一個(gè) cpu 暴漲的問題,我們就需要使用pprof 套件中的 profile 工具來定位出哪個(gè)函數(shù)占用 cpu 比較多。
這里我們需要明確一下:
我們說是要定位一個(gè)程序的 cpu 消耗問題,但是這里其實(shí)隱藏了一個(gè)時(shí)間段概念,即:“我們要定位什么時(shí)間段內(nèi)的 cpu 消耗”? - 這里當(dāng)然無法定位過去時(shí)間的 cpu 消耗,我們只能定位從運(yùn)行時(shí)間開始一段時(shí)間內(nèi)的 cpu 消耗,即“從現(xiàn)在開始的 30s 作為取樣樣本,來分析這段時(shí)間內(nèi)的 cpu 耗時(shí)分布”。所以我們需要給 pprof 端口下命令,來定位抓取 30s 內(nèi)的消耗,同樣,我們使用 curl 命令即可。
curl -o profile.log localhost:6060/debug/pprof/profile?seconds=30
這里的兩個(gè)參數(shù),seconds - 指定樣本時(shí)間,很好理解。而這里輸出的 profile.log 打開是一種二進(jìn)制亂碼,profile 是沒有 debug 參數(shù)的,它只能通過 go tool pprof 工具打開。如果我們安裝 go 工具,或者下載 profile.log 到有 go 工具的環(huán)境,我們才能閱讀。- 這里就是比較麻煩的一點(diǎn)了。
不過所幸,閱讀的方法同 goroutine 的 top 命令類似,唯一不同的是,這里的資源換成了函數(shù)消耗的 cpu 時(shí)間。
(pprof) top100
Showing nodes accounting for 4.26s, 79.92% of 5.33s total
Dropped 370 nodes (cum <= 0.03s)
Showing top 100 nodes out of 395
flat flat% sum% cum cum%
0.49s 9.19% 9.19% 0.73s 13.70% runtime.scanobject
0.43s 8.07% 17.26% 0.43s 8.07% runtime.futex
0.32s 6.00% 23.26% 0.35s 6.57% syscall.Syscall
0.23s 4.32% 27.58% 0.53s 9.94% runtime.mallocgc
0.19s 3.56% 31.14% 0.19s 3.56% runtime.epollwait
0.13s 2.44% 33.58% 0.13s 2.44% runtime.greyobject
0.13s 2.44% 36.02% 0.14s 2.63% runtime.heapBitsSetType
0.13s 2.44% 38.46% 0.13s 2.44% runtime.memmove
0.12s 2.25% 40.71% 0.12s 2.25% runtime.findObject
0.12s 2.25% 42.96% 0.12s 2.25% runtime.usleep
0.10s 1.88% 44.84% 0.80s 15.01% runtime.findrunnable
0.10s 1.88% 46.72% 0.26s 4.88% runtime.stealWork
0.08s 1.50% 48.22% 0.08s 1.50% [libc-2.28.so]
0.08s 1.50% 49.72% 0.11s 2.06% runtime.mapaccess2
0.08s 1.50% 51.22% 0.08s 1.50% runtime.memclrNoHeapPointers
0.06s 1.13% 52.35% 0.11s 2.06% go.uber.org/zap/zapcore.(*jsonEncoder).tryAddRuneSelf
0.05s 0.94% 53.28% 0.19s 3.56% google.golang.org/protobuf/internal/impl.(*MessageInfo).unmarshalPointer
0.05s 0.94% 54.22% 0.06s 1.13% runtime.lock2
0.05s 0.94% 55.16% 0.15s 2.81% runtime.mapassign_faststr
0.05s 0.94% 56.10% 0.05s 0.94% runtime.step
如何分析?如果你看了 goroutine 那節(jié),就知道這是一個(gè)套路了,如果沒有看,往上翻。 - 1 看 cum 值,2 過濾出非系統(tǒng)函數(shù)調(diào)用,第一個(gè)占用最大的應(yīng)用函數(shù),就是目標(biāo)函數(shù)了,然后分析代碼,有沒有什么 for 死循環(huán),有沒有什么耗 cpu 的行為,就是它了。
在沒有 goroutine 泄漏的前提下,內(nèi)存為什么占用這么大呢?我們需要 pprof 套件中的兩個(gè)工具,allocs 和 heap。
為什么有兩個(gè)呢?它們兩個(gè)的差別就是allocs 代表的是某個(gè)函數(shù)歷史創(chuàng)建的所有的內(nèi)存(包括已經(jīng)回收了的),而 heap 代表的是當(dāng)前活躍的內(nèi)存占用對象。所以我們能看到 allocs 中的數(shù)值會(huì)比 heap 大了非常多,一般我們使用 heap 比較多。
他們兩個(gè)命令都有 debug 參數(shù),但是 debug 參數(shù)只能為 1 或者沒有。同 goroutine 命令一樣,沒有 debug 參數(shù)就只能通過 go tool 來分析,會(huì)麻煩些。
我們這里分析一個(gè)下 debug=1 的可讀性文本:
curl -o allocs1.log localhost:6606/debug/pprof/allocs?debug=1
這里說一下,allocs 和 heap 兩個(gè)命令使用 debug=1 的參數(shù),顯示的內(nèi)容其實(shí)都是一樣的,可讀性文本里面不僅包含了當(dāng)前活躍的內(nèi)存信息,也包含了歷史創(chuàng)建的內(nèi)存信息。比如下面這個(gè)展示:
heap profile: 169: 18537048 [35672473: 126318298296] @ heap/1048576
1: 1810432 [1: 1810432] @ 0x41dbee 0x41d99c 0x1b75345 0x456203 0x456151 0x456151 0x456151 0x448ec6 0x47aa41
# 0x1b75344 github.com/mozillazg/go-pinyin.init+0x3c4 /root/go/pkg/mod/github.com/mozillazg/[email protected]/pinyin_dict.go:5
# 0x456202 runtime.doInit+0x122 /usr/go/src/runtime/proc.go:6498
# 0x456150 runtime.doInit+0x70 /usr/go/src/runtime/proc.go:6475
# 0x456150 runtime.doInit+0x70 /usr/go/src/runtime/proc.go:6475
# 0x456150 runtime.doInit+0x70 /usr/go/src/runtime/proc.go:6475
# 0x448ec5 runtime.main+0x1e5 /usr/go/src/runtime/proc.go:238
第一行
169: 18537048 [35672473: 126318298296]
告訴我們,這個(gè)進(jìn)程目前活躍 169 個(gè)對象,占用字節(jié)數(shù)18537048,歷史有過 35672473 個(gè)對象,占用字節(jié)數(shù) 126318298296。 1048576 表示內(nèi)存采樣的頻率,默認(rèn)采樣頻率是512kb 。這里采樣頻率是兩倍的內(nèi)存采樣頻率(1024 x 1024)。
第二行
1: 1810432 [1: 1810432]
告訴我們,下面這個(gè)堆;钴S 1 個(gè)對象,占用字節(jié)數(shù) 1810432,歷史只有過 1 個(gè)對象,占用字節(jié)數(shù) 1810432。
但是實(shí)話說,分析內(nèi)存如果用這個(gè) curl 的方式,會(huì)看的很累,因?yàn)樗呐判虿]有任何太多意義,比如占用最大內(nèi)存的,可能是非常合理的,要看到后面幾名才能知道哪個(gè)占用內(nèi)存大不合理。
還是通過 go tool 工具來查看top 更為快捷:
以下是 allocs 命令的 top
(pprof) top10
Showing nodes accounting for 8273GB, 47.42% of 17444.69GB total
Dropped 2963 nodes (cum <= 87.22GB)
Showing top 10 nodes out of 267
flat flat% sum% cum cum%
1948.96GB 11.17% 11.17% 1948.96GB 11.17% go.uber.org/zap/buffer.(*Buffer).AppendByte (inline)
1260.22GB 7.22% 18.40% 1260.22GB 7.22% my-go/codec.MetaData.Clone
895.47GB 5.13% 23.53% 3649.40GB 20.92% go.uber.org/zap/zapcore.(*ioCore).Write
894.80GB 5.13% 28.66% 987.35GB 5.66% google.golang.org/protobuf/internal/encoding/text.(*Encoder).WriteName (inline)
813.76GB 4.66% 33.32% 813.76GB 4.66% go.uber.org/zap/internal/bufferpool.init.func1
780.93GB 4.48% 37.80% 3200.55GB 18.35% fmt.Sprintf
以下是 heap 命令的 top
Showing nodes accounting for 47.06MB, 50.44% of 93.30MB total
Showing top 10 nodes out of 487
flat flat% sum% cum cum%
12.93MB 13.86% 13.86% 12.93MB 13.86% google.golang.org/protobuf/internal/strs.(*Builder).AppendFullName
9.06MB 9.71% 23.57% 17.50MB 18.75% google.golang.org/protobuf/internal/filedesc.(*Message).unmarshalFull
6.11MB 6.54% 30.12% 6.11MB 6.54% github.com/rcrowley/go-metrics.newExpDecaySampleHeap
3.50MB 3.75% 33.87% 6.50MB 6.97% my-go/pkg/model/pb.v2ServiceInstancesToV1
3.01MB 3.22% 37.09% 3.01MB 3.22% github.com/Shopify/sarama.(*asyncProducer).newPartitionProducer
3MB 3.22% 40.31% 3MB 3.22% my-go/pkg/model/pb.v2StringToV1WrapperString (inline)
從這里也能看出,alloc 的數(shù)值比 heap 大了不少,相比之下,heap 命令對于分析當(dāng)前服務(wù)內(nèi)存占用更有作用。
pprof 套件中還有很多工具,有哪些工具呢?不用費(fèi)心去記,直接 curl 也能看出來。
curl http://localhost:6606/debug/pprof/
輸出的 html 有如下的有用信息:
204775 allocs
0 block
0 cmdline
1882 goroutine
204775 heap
0 mutex
0 profile
18 threadcreate
0 trace
allocs: A sampling of all past memory allocations
block: Stack traces that led to blocking on synchronization primitives
cmdline: The command line invocation of the current program
goroutine: Stack traces of all current goroutines
heap: A sampling of memory allocations of live objects. You can specify the gc GET parameter to run GC before taking the heap sample.
mutex: Stack traces of holders of contended mutexes
profile: CPU profile. You can specify the duration in the seconds GET parameter. After you get the profile file, use the go tool pprof command to investigate the profile.
threadcreate: Stack traces that led to the creation of new OS threads
trace: A trace of execution of the current program. You can specify the duration in the seconds GET parameter. After you get the trace file, use the go tool trace command to investigate the trace.
讓 Claude 幫我們解釋下:
這些是 Go (golang)
pprof
包提供的不同的分析工具,每個(gè)工具都有特定的目的,用于分析和調(diào)試 Go 應(yīng)用程序的性能問題。以下是每個(gè)工具的功能:
seconds
GET 參數(shù)指定分析的持續(xù)時(shí)間。
seconds
GET 參數(shù)指定追蹤的持續(xù)時(shí)間。
這些工具對于理解和優(yōu)化 Go 應(yīng)用程序的性能非常有價(jià)值,特別是在處理復(fù)雜的并發(fā)系統(tǒng)或內(nèi)存密集型工作負(fù)載時(shí)。
其他幾個(gè)工具我基本上很少使用了,等有用的時(shí)候再記錄吧,或許一直也用不到,那么正應(yīng)了那句古話:書到不用時(shí)方嫌多。
https://mp.weixin.qq.com/s/_MWtrXYWm5pzT8Rt3r_YDQ
https://www.cnblogs.com/hobbybear/p/17292713.html
https://go.dev/doc/diagnostics
https://jvns.ca/blog/2017/09/24/profiling-go-with-pprof/
https://segmentfault.com/a/1190000016412013
https://segmentfault.com/a/1190000019222661
https://blog.wolfogre.com/posts/go-ppof-practice/
機(jī)器學(xué)習(xí):神經(jīng)網(wǎng)絡(luò)構(gòu)建(下)
閱讀華為Mate品牌盛典:HarmonyOS NEXT加持下游戲性能得到充分釋放
閱讀實(shí)現(xiàn)對象集合與DataTable的相互轉(zhuǎn)換
閱讀鴻蒙NEXT元服務(wù):論如何免費(fèi)快速上架作品
閱讀算法與數(shù)據(jù)結(jié)構(gòu) 1 - 模擬
閱讀5. Spring Cloud OpenFeign 聲明式 WebService 客戶端的超詳細(xì)使用
閱讀Java代理模式:靜態(tài)代理和動(dòng)態(tài)代理的對比分析
閱讀Win11筆記本“自動(dòng)管理應(yīng)用的顏色”顯示規(guī)則
閱讀本站所有軟件,都由網(wǎng)友上傳,如有侵犯你的版權(quán),請發(fā)郵件[email protected]
湘ICP備2022002427號(hào)-10 湘公網(wǎng)安備:43070202000427號(hào)© 2013~2025 haote.com 好特網(wǎng)