- runtime/pprof:采集程序(非 Server)的运行数据进行分析,用于可结束的代码块,如一次编解码操作等
- net/http/pprof:采集 HTTP Server 的运行时数据进行分析。用于不可结束的代码块,如 web 应用等
使用场景
分析内存泄漏‘
总结
go中的内存泄露一般都是goroutine泄露,goroutine没有被关闭,或者没有添加超时控制,让goroutine一只处于阻塞状态,不能被GC。
暂时性内存泄漏
- 获取长字符串中的一段导致长字符串未释放
- 获取长slice中的一段导致长slice未释放
- 在长slice新建slice导致泄漏
永久性内存泄漏
- goroutine泄漏
- time.Ticker未关闭导致泄漏
- Finalizer导致泄漏
- Deferring Function Call导致泄漏
常见Pprof使用方式(包net/http/pprof):
1. 网络访问方式 ,go tool 查看火焰图
程序中,单独起一个协程,引入包_ "net/http/pprof" 新监听另一个端口作为pprof访问,如下图方法
func setDebug() error {
if config.C.General.Debug != "" {
port := strings.Split(config.C.General.Debug, ":")[1]
log.WithFields(log.Fields{
"profile": "http://127.0.0.1:" + port + "/debug/pprof/profile",
"goroutines": "http://127.0.0.1:" + port + "/goroutines",
}).Info("open prof debug")
go func() {
http.HandleFunc("/goroutines", func(w http.ResponseWriter, r *http.Request) {
num := strconv.FormatInt(int64(runtime.NumGoroutine()), 10)
w.Write([]byte(num))
})
http.ListenAndServe(config.C.General.Debug, nil)
}()
}
return nil
}
或者直接共用程序监听端口。引入包_ "net/http/pprof"包即可,如下
import (
"fmt"
"net/http"
_ "net/http/pprof"
)
func HelloWorld(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello world")
}
func main() {
http.HandleFunc("/", HelloWorld)
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println(err)
}
}
先执行程序,然后再起一个终端窗口执行go tool指令。记得先安装火焰图工具,方便查看,安装方式在下方。
go tool pprof -http=:9000 http://localhost:88/debug/pprof/goroutine?debug=1(要监听的服务地址)
9000:表示你最终查看火焰图的端口,执行后会使用默认浏览器,自动打开网页
#查看所有当前goroutine的堆栈跟踪
go tool pprof http://127.0.0.1:8080/debug/pprof/goroutine
# wait 120s
go tool pprof http://127.0.0.1:8080/debug/pprof/profile?seconds=120
火焰图安装地址:
下载安装可视化图形软件工具 graphviz :https://graphviz.org/download/。可自行选择Linux、Windows、Mac等对应版本,并且记得将该工具添加到系统环境变量中,否则会报错:failed to execute dot. Is Graphviz installed? Error: exec: "dot": executable file not found in %PATH%
采集前也可以先进行压测,在压测过程中再去收集,有助于分析:
go-wrk -c=400 -t=8 -n=100000 http://127.0.0.1:8000/api/getReq
- 400个连接,8个线程, 模拟10w次请求
2. gin框架,使用封装好的
"github.com/gin-contrib/pprof"
框架里的因为封装好了。启动程序之后,可以直接浏览器访问,不需要用go tool执行了 (可以在程序开启后,先执行压测指令)
直接访问程序http://localhost:88/debug/pprof/
具体参数的含义
- allocs: 内存分配情况的抽象情况
- block: 阻塞堆栈的采样信息
- cmdline: 程序启动命令及其参数
- goroutine: 当前协程的堆栈信息
- heap: 堆内存的采样信息
- mutex: 锁竞争的采样信息
- profile: cpu使用情况的采样信息
- threadcreate: 系统程序创建情况的采样信息
- trace: 程序运行的跟踪信息
在浏览器中我们可以看到图形化的函数调用堆栈信息。右上角的VIEW
栏有一些选项,可以点开查看,Flame Graph
就是传说中的火焰图。
常见Pprof使用方式(包runtime/pprof):
工具型应用调用比较简单,主要是用 runtime/pprof库,将数据写入文件中:,然后查看
package main
import (
"flag"
"log"
"os"
"runtime/pprof"
"sync"
)
var (
cpu string
mem string
)
func init() {
flag.StringVar(&cpu, "cpu", "", "write cpu profile to file")
flag.StringVar(&mem, "mem", "", "write mem profile to file")
}
func main() {
flag.Parse()
//采样 CPU 运行状态
if cpu != "" {
f, err := os.Create(cpu)
if err != nil {
log.Fatal(err)
}
_ = pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
var wg sync.WaitGroup
wg.Add(100)
for i := 0; i < 100; i++ {
go workOnce(&wg)
}
wg.Wait()
//采样内存状态
if mem != "" {
f, err := os.Create(mem)
if err != nil {
log.Fatal(err)
}
_ = pprof.WriteHeapProfile(f)
f.Close()
}
}
func counter() {
slice := make([]int, 0)
var c int
for i := 0; i < 10000; i++ {
c = i + 1 + 2 + 3 + 4 + 5
slice = append(slice, c)
}
_ = slice
}
func workOnce(wg *sync.WaitGroup) {
counter()
wg.Done()
}
go run main.go --cpu=cpu.pprof --mem=mem.pprof
go tool pprof cpu.pprof
点击文件,打开。(前提是安装了graphviz工具)
如果没有安装graphviz
,会出现Could not execute dot;may need to install graphviz
。安装graphviz的方式如下:
brew install graphviz # for macos
apt-get install graphviz # for ubuntu
yum install graphviz # for centos
trace性能工具使用
1. 使用包 "runtime/trace"
package main
import (
"fmt"
"log"
"os"
"runtime/trace"
"sync"
)
func main() {
//runtime.GOMAXPROCS(1)
// 1. 创建trace持久化的文件句柄
f, err := os.Create("trace.out")
if err != nil {
log.Fatalf("failed to create trace output file: %v", err)
}
defer func() {
if err := f.Close(); err != nil {
log.Fatalf("failed to close trace file: %v", err)
}
}()
// 2. trace绑定文件句柄
if err := trace.Start(f); err != nil {
log.Fatalf("failed to start trace: %v", err)
}
defer trace.Stop()
var wg sync.WaitGroup
wg.Add(2)
go func() {
fmt.Println(`Hello`)
wg.Done()
}()
go func() {
fmt.Println(`World`)
wg.Done()
}()
wg.Wait()
}
先开窗口执行
go run main.go
再打开一个窗口执行
go tool trace -http=localhost:8080 trace.out
2023/01/03 11:01:57 Parsing trace...
2023/01/03 11:01:57 Splitting trace...
2023/01/03 11:01:57 Opening browser. Trace viewer is listening on http://127.0.0.1:8888
View trace:查看跟踪
Goroutine analysis:Goroutine 分析,查看具体的携程数
Network blocking profile:网络阻塞概况
Synchronization blocking profile:同步阻塞概况
Syscall blocking profile:系统调用阻塞概况
Scheduler latency profile:调度延迟概况,这个是常用的,用来分析耗时在哪里
User defined tasks:用户自定义任务
User defined regions:用户自定义区域
Minimum mutator utilization:最低 Mutator 利用率
参考:
Go语言内存泄露_cqu_jiangzhou的博客-CSDN博客_go内存泄露
http://liumurong.org/2019/12/gin_pprof/
Go pprof 性能分析工具 - 详细使用图解:_dreamer'~的博客-CSDN博客_pprof图怎么看
golang性能分析之trace_raoxiaoya的博客-CSDN博客_golang trace