返回归档
Golang

pprof的使用

文章目录

什么是pprof

pprof 是 Go 提供的性能剖析工具,允许开发者查看 CPU、内存、Goroutine 以及其他资源的使用情况

启动pprof

import (
    _ "net/http/pprof"
    "log"
    "net/http"
)

func main() {
    go func() {
        log.Println(http.ListenAndServe("localhost:6060", nil))
    }()
    // 其他代码...
}

一旦你的应用程序运行并且 pprof 启用后,你可以通过浏览器或命令行工具访问 pprof 数据。常见的端点包括:

  • CPU profile: http://localhost:6060/debug/pprof/profile?seconds=30
  • Heap profile: http://localhost:6060/debug/pprof/heap
  • Goroutine profile: http://localhost:6060/debug/pprof/goroutine
  • Block profile: http://localhost:6060/debug/pprof/block

使用pprof排查heap饱满

模拟heap爆满的场景,github地址为 codeCollection/pprof at main · Forrest-Tao/codeCollection (github.com) 其中的文件可以自己模拟一个非常大的文件

#运行go 程序 
go mod tidy && go run . 


//导出文件的方式 具体使用见下方
curl -o heap.out http://localhost:6060/debug/pprof/heap 


#在本地8080端口发起请求
http://localhost:8080/readall


#不导出文件的方式,直接请求
go tool pprof http://localhost:6060/debug/pprof/heap   

#进入pprof后,top 10 查看前10的heap分配情况
top 10

观察系统 内存变化

htop

查看具体的heap.out 文件

 curl -o heap.out http://127.0.0.1:6060/debug/pprof/heap
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  3257    0  3257    0     0  1060k      0 --:--:-- --:--:-- --:--:-- 1060k
 go tool pprof heap.out
File: demo
Type: inuse_space
Time: Aug 22, 2024 at 8:07pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top 10
Showing nodes accounting for 2.61GB, 100% of 2.61GB total
Dropped 9 nodes (cum <= 0.01GB)
      flat  flat%   sum%        cum   cum%
    2.61GB   100%   100%     2.61GB   100%  io.ReadAll
         0     0%   100%     2.61GB   100%  github.com/gin-gonic/gin.(*Context).Next (inline)
         0     0%   100%     2.61GB   100%  github.com/gin-gonic/gin.(*Engine).ServeHTTP
         0     0%   100%     2.61GB   100%  github.com/gin-gonic/gin.(*Engine).handleHTTPRequest
         0     0%   100%     2.61GB   100%  github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1
         0     0%   100%     2.61GB   100%  github.com/gin-gonic/gin.LoggerWithConfig.func1
         0     0%   100%     2.61GB   100%  io/ioutil.ReadAll (inline)
         0     0%   100%     2.61GB   100%  main.testReadAll
         0     0%   100%     2.61GB   100%  net/http.(*conn).serve
         0     0%   100%     2.61GB   100%  net/http.serverHandler.ServeHTTP
(pprof) list main.testReadAll  # 列出方法
Total: 2.61GB
ROUTINE ======================== main.testReadAll in /mnt/c/Users/xiaoxi/GolandProjects/codeCollection/pprof/main.go
         0     2.61GB (flat, cum)   100% of Total
         .          .     40:func testReadAll(c *gin.Context) {
         .          .     41:   //fmt.Println("in testReadAll")
         .          .     42:   file, err := os.Open("./cka.zip")
         .          .     43:   if err != nil {
         .          .     44:           panic(err)
         .          .     45:   }
         .          .     46:   defer file.Close()
         .          .     47:   //simulate onLine err
         .     2.61GB     48:   bytes, err := ioutil.ReadAll(file)  #这有写明白 
         .          .     49:   if err != nil {
         .          .     50:           panic(err)
         .          .     51:   }
         .          .     52:   c.Writer.Write(bytes)
         .          .     53:   if err != nil {
(pprof)

网页可视化

默认 net/http/pprof 挂在 /debug/pprof/。本机把 profile 拉下来用交互 UI:

go tool pprof -http=:8080 http://127.0.0.1:6060/debug/pprof/heap

浏览器里看 inuse_space 的火焰图 / top / source。线上不要把这个端口暴露到公网。

上面的 heap 案例已经能定位:ioutil.ReadAll 一次把文件读进内存,单次请求堆上 2.61GB。改成 io.Copy 流式写出,再抓一次 heap,这条栈应从 top 消失。

分析过程

  1. 先看 alloc_spaceinuse_space 是谁涨:前者是累计分配,后者是还活着的。
  2. 对这条接口打一次 heap,按 inuse_space 排序,对上源码行。
  3. 改掉整包读入后复测;RSS 下来且同一栈不再占 top,才算闭环。

ref

bookmark