go 性能分析pprof和trace

  • 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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值