目录

Golang 性能分析神器 pprof 最全图文教程

一、简介

pprof 是 Go 标准库提供的性能剖析工具,能够帮助开发者分析程序在运行时的各项性能指标。在我日常的 Go 开发中,pprof 是排查性能问题的首选工具。

核心优势:

  • 零侵入:只需导入包即可使用,无需修改业务代码
  • 低开销:基于采样机制,对生产环境影响极小
  • 可视化:提供火焰图等直观的分析界面
  • 全面性:涵盖 CPU、内存、协程、锁等多个维度

我在实际项目中使用 pprof 成功定位并解决了多次内存泄漏和 CPU 高占用问题,接下来分享一下具体的使用方法。

二、用法

(一)开启 pprof

集成 pprof 非常简单,我通常采用以下方式:

第一步:导入包

import (
    _ "net/http/pprof"
)

第二步:启动 HTTP 服务

go func() {
        logs.Info(http.ListenAndServe(":30552", nil))
    }()

完整示例代码:

import _ "net/http/pprof"

func StartPprof() {
    go func() {
        // 启动 pprof 服务,端口可以根据实际情况调整
        logs.Info(http.ListenAndServe(":30552", nil))
    }()
}

func main(){
  // 其他初始化代码...

  // 启动 pprof 服务(建议在开发和测试环境使用)
  StartPprof() 

  // 启动主程序
  err = router.Run(address)
    if err != nil {
        panic(err)
    }
}

⚠️ 注意事项

  • 端口号避免与业务端口冲突
  • 生产环境建议通过配置开关控制是否启动
  • 可以配合防火墙规则,限制访问来源

### (二)用法一:浏览器直接访问

最简单的方式是通过浏览器直接访问 pprof 提供的接口:

```bash
# pprof 首页入口
http://0.0.0.0:30552/debug/pprof/ 

/img/go-pprof/0201.png
pprof

实用的分析接口:

# 查看内存堆栈(哪些函数持续占用内存)
http://0.0.0.0:30552/debug/pprof/heap?debug=1

# 查看内存分配情况(哪些函数频繁分配内存)
http://0.0.0.0:30552/debug/pprof/allocs?debug=1

# 查看协程总览
http://0.0.0.0:30552/debug/pprof/goroutine?debug=1
# 查看协程详细堆栈(定位阻塞问题)
http://0.0.0.0:30552/debug/pprof/goroutine?debug=2

# 阻塞分析
http://0.0.0.0:30552/debug/pprof/block?debug=1
http://0.0.0.0:30552/debug/pprof/block?debug=2

# 锁竞争分析
http://0.0.0.0:30552/debug/pprof/mutex?debug=1
http://0.0.0.0:30552/debug/pprof/mutex?debug=2

⚠️ 重要提示

  • 必须加上 debug 参数,否则会直接下载二进制文件
  • debug=1 显示概要信息,debug=2 显示完整堆栈信息
  • 这种方式适合快速查看,但不够直观

(三)用法二:go tool pprof(强烈推荐)

浏览器直接访问只能看到文本信息,不够直观。我在实际工作中主要使用 go tool pprof 生成火焰图,能够更快速地定位性能瓶颈。

前置条件:需要安装 graphviz 才能正常展示火焰图

graphviz 安装教程请参考:开源的图形可视化工具graphviz安装教程

基本用法:

go tool pprof <参数> <pprof 数据源>

# 示例:启动 web 服务展示最近 60 秒的 CPU 性能数据
go tool pprof -http=0.0.0.0:8081 -seconds=60 http://0.0.0.0:30552/debug/pprof/profile

参数说明:

  • -http= 指定 IP:端口,启动 web 服务展示可视化界面。如果不指定,会下载数据文件并进入命令行交互模式

  • -seconds= 指定采样时长,例如 -seconds=60 表示采样 60 秒的数据。不指定则显示进程启动以来的累计数据

数据源说明:

可以是 pprof 服务接口:http://0.0.0.0:30552/debug/pprof/profile

也可以是本地数据文件:xxx.pb.gz

如何获取 pprof 数据文件:

# 直接访问接口会下载数据文件(同时进入命令行交互模式)
go tool pprof http://0.0.0.0:30552/debug/pprof/profile    # CPU 性能数据
go tool pprof http://0.0.0.0:30552/debug/pprof/heap       # 内存堆栈数据
go tool pprof http://0.0.0.0:30552/debug/pprof/allocs     # 内存分配数据
go tool pprof http://0.0.0.0:30552/debug/pprof/goroutine  # 协程数据
go tool pprof http://0.0.0.0:30552/debug/pprof/block      # 阻塞分析数据
go tool pprof http://0.0.0.0:30552/debug/pprof/mutex      # 锁竞争数据

常用的分析命令:

# CPU 耗时分析(最常用)
go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/profile

# 内存堆栈分析(排查内存泄漏)
go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/heap

# 内存分配分析(查找频繁分配的函数)
go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/allocs

# 协程分析(排查协程泄漏)
go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/goroutine

# 阻塞分析
go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/block

# 锁竞争分析
go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/mutex

接下来我会详细讲解每种分析方式的具体操作步骤和解读方法。

三、CPU 耗时分析实战

这是我使用最频繁的功能,用于定位哪些函数消耗了大量 CPU 时间。

(一)执行命令

go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/profile

执行后浏览器会自动打开 0.0.0.0:8081/ui(如果没有自动打开,手动访问即可)

这里没有加 -seconds= 参数,会显示进程启动以来的累计 CPU 数据

(二)火焰图解读

火焰图示例:

/img/go-pprof/0301.png
CPU耗时分析

关键元素说明:

  • 方框:每个方框代表一个函数,方框越大、颜色越深,表示该函数占用的 CPU 资源越多

  • 颜色含义

    • 红色:CPU 占用较高的热点函数
    • 绿色:CPU 占用相对较低
    • 颜色深度:直接反映 CPU 占用程度
  • 方框粗细:和颜色类似,越粗表示 CPU 占用越多。如果一个函数又红又粗,说明它是主要的性能瓶颈

  • 箭头类型

    类型 示例 含义
    普通箭头 FuncA ──→ FuncB 函数直接调用关系
    带时间标签的箭头 FuncA ── 50ms → FuncB FuncA 调用 FuncB 耗时 50ms
    虚线箭头 FuncA ···→ runtime.mallocgc 编译器隐式插入的操作(如内存分配)
    双向箭头 FuncA <──> FuncB 相互调用或递归调用,需要特别关注是否有性能问题
  • (inline) 标记:表示该函数被编译器内联优化处理,这通常是好事

  • 数字含义

    /img/go-pprof/0302.png
    函数耗时分析

(三)Top 视图分析

访问 0.0.0.0:8081/ui/top 可以看到指标排序视图:

/img/go-pprof/0303.png
CPU指标分析

这个视图和火焰图展示的是同一份数据,只是呈现方式不同,更便于查看具体的代码位置。

指标详解:

  1. flat:函数本身(不包括它调用的子函数)直接消耗的 CPU 时间,单位 ms
  2. flat%flat / 总时间 × 100%
  3. sum%:当前行及之前所有行的 flat% 累加值
  4. cum:函数总耗时(包含所有子函数调用),单位 ms
  5. cum%cum / 总时间 × 100%

(四)实战经验总结

根据我的实际使用经验:

  • 看火焰图找热点:哪个框又红又粗,哪个就是 CPU 消耗大户。顺着箭头找到调用链,就能定位到具体代码位置

  • 用 Top 视图确认位置:如果火焰图中函数名不够明确,切换到 Top 视图,可以看到完整的包路径和文件名

  • 优化建议

    • 优先优化 flat 值大的函数(直接耗时多)
    • 关注 cum 值大但 flat 值小的函数(子函数调用多)
    • 查看具体优化方法可参考文末的【常见优化措施】

四、内存堆栈分析实战

用于排查内存泄漏问题,查看哪些函数长期占用内存不释放。

(一)执行命令

go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/heap

浏览器会自动打开 0.0.0.0:8081/ui

显示进程启动以来的内存堆栈累计数据

(二)火焰图分析

/img/go-pprof/0401.png
pprof内存堆栈分析

分析方法和 CPU 火焰图类似,重点关注又红又粗的方框,这些函数占用了大量常驻内存。

(三)指标分析

/img/go-pprof/0402.png
pprof内存堆栈指标分析

指标详解:

  1. flat:函数本身直接占用的堆内存(不包括子函数),单位 KB
  2. flat%flat / 总内存 × 100%
  3. sum%:当前行及之前所有行的 flat% 累加
  4. cum:函数总占用内存(包含所有子函数),单位 KB
  5. cum%cum / 总内存 × 100%

(四)实战技巧

在我实际排查内存泄漏时的经验:

  • 定位常驻内存:火焰图中又红又粗的函数就是内存泄漏的嫌疑对象

  • 结合 Top 视图:通过 Top 视图找到具体的代码文件和行号

  • 常见原因

    • 全局变量或缓存未设置淘汰机制
    • goroutine 泄漏导致相关数据无法回收
    • 闭包引用导致对象无法释放
    • 定时器未正确关闭
  • 优化建议:详见文末【常见优化措施】

五、内存分配分析实战

和内存堆栈分析不同,这个功能主要用于查找频繁分配内存的函数,即使这些内存很快被释放了,频繁分配也会给 GC 带来压力。

(一)执行命令

go tool pprof -http=0.0.0.0:8081 http://0.0.0.0:30552/debug/pprof/allocs

浏览器会自动打开 0.0.0.0:8081/ui

显示进程启动以来的内存分配累计数据

(二)火焰图分析

火焰图示例:

/img/go-pprof/0501.png
pprof内存分配分析

从这个火焰图可以看出:

  1. OperatuonRecord 存在循环调用,需要确认这是否符合业务预期
  2. CreateImgCompress 函数内存分配量很大,需要深入分析其调用的子函数,找出真正的问题点

(三)指标分析

/img/go-pprof/0502.png
pprof内存分配指标分析

指标详解:

  1. flat:函数本身分配的内存(不包括子函数),单位 KB
  2. flat%flat / 总分配内存 × 100%
  3. sum%:当前行及之前所有行的 flat% 累加
  4. cum:函数总分配内存(包含所有子函数),单位 KB
  5. cum%cum / 总分配内存 × 100%

(四)实战经验

我在项目中遇到过几次内存频繁分配的问题:

案例 1:字符串拼接 在循环中使用 += 拼接字符串,导致大量内存分配。改用 strings.Builder 后性能提升 10 倍以上。

案例 2:切片扩容 未预分配切片容量,导致频繁触发 growslice。预估容量后使用 make([]T, 0, cap) 显著降低了分配次数。

案例 3:JSON 序列化 高并发场景下使用标准库 encoding/json,CPU 和内存压力都很大。替换为 json-iterator 后问题解决。

优化建议

  • 关注 flat 值大的函数
  • 查找代码中是否有循环内的内存分配
  • 考虑使用对象池(sync.Pool)复用对象
  • 详细优化方法见文末【常见优化措施】

六、协程分析实战

协程泄漏是 Go 程序中常见但容易被忽视的问题,通过 pprof 可以快速发现。

(一)执行命令

协程分析直接通过浏览器访问即可,无需 go tool pprof

# 查看协程总数和简要信息
http://0.0.0.0:30552/debug/pprof/goroutine?debug=1 

# 查看每个协程的详细堆栈信息
http://0.0.0.0:30552/debug/pprof/goroutine?debug=2

debug 参数说明

  • debug=1:显示协程总数和状态概览
  • debug=2:显示每个协程的完整调用栈

(二)数据分析

协程总览(debug=1):

/img/go-pprof/0601.png
pprof协程分析

从图中可以看到进程当前有 27 个协程。协程数量本身没有绝对的好坏,关键是要确认这些协程是否符合业务预期。

协程详细信息(debug=2):

/img/go-pprof/0602.png
pprof协程堆栈信息

这里会列出所有协程的运行堆栈。需要注意的是,协程 ID 越大不代表有问题,重点是看协程所处的状态和堆栈信息。

(三)实战案例:发现协程泄漏

我在一个项目中发现了一个意外的 trace 协程:

/img/go-pprof/0603.png
pprof协程泄漏

通过堆栈信息,我发现是某个第三方包启动的协程。排查代码后确认这个功能并没有实际使用,于是:

  1. 注释掉相关的导入包
  2. 删除未使用的初始化代码
  3. 重启服务后该协程消失

(四)排查协程泄漏的经验

根据我的实际经验,协程泄漏通常有以下几种情况:

1. Channel 阻塞

// 错误示例:goroutine 会永久阻塞
ch := make(chan int)
go func() {
    ch <- 1  // 没有接收方,永久阻塞
}()

2. 死循环未退出

// 错误示例:没有退出条件
go func() {
    for {
        // 业务逻辑
    }
}()

3. 等待锁或资源

// 错误示例:等待永远不会释放的锁
go func() {
    mutex.Lock()
    // 某些条件下 unlock 未执行
}()

排查建议

  • 定期检查协程数量是否持续增长
  • 关注堆栈中 chan send/receiveselectLock 等关键字
  • 为协程添加 context 超时机制
  • 确保每个 goroutine 都有明确的退出条件

七、锁竞争分析

在高并发场景下,锁竞争可能成为性能瓶颈。pprof 可以帮助我们找出锁竞争最激烈的地方。

(一)执行命令

http://0.0.0.0:30552/debug/pprof/mutex?debug=1

锁分析也是直接通过浏览器访问 pprof 接口

(二)数据分析

/img/go-pprof/0701.png
pprof锁分析

(三)实战经验

从界面可以看到锁竞争次数最多的堆栈信息,通过这些信息可以:

  1. 确认是否是业务代码:如果是第三方库的锁竞争,可能需要考虑更换实现
  2. 定位代码位置:找到具体的文件和行号
  3. 评估优化必要性:不是所有锁竞争都需要优化,要结合实际性能影响判断

常见优化方案

  • 缩小锁的粒度(减少临界区代码)
  • 使用读写锁 sync.RWMutex 替代互斥锁
  • 考虑使用 sync.Map 或分段锁
  • 使用 channel 代替共享内存通信

八、阻塞分析

阻塞分析可以帮助我们找出程序中阻塞操作最严重的地方,比如 channel 操作、网络 I/O 等。

(一)执行命令

http://0.0.0.0:30552/debug/pprof/block?debug=1

阻塞分析同样通过浏览器直接访问 pprof 接口

(二)分析思路

阻塞分析展示的是程序中各种阻塞操作的统计信息,主要包括:

  • Channel 发送/接收阻塞:找出 channel 操作的瓶颈
  • 网络 I/O 阻塞:定位网络请求慢的地方
  • 系统调用阻塞:发现系统级别的阻塞

优化建议

  • 增加 channel 缓冲区大小
  • 为阻塞操作添加超时机制
  • 使用异步处理减少阻塞
  • 优化数据库查询和网络请求

九、单元测试中使用 pprof

除了在运行中的服务上使用 pprof,我们还可以在单元测试中生成性能数据,这对于优化特定函数非常有用。

(一)示例代码

package main

import (
    "fmt"
    "os"
    "runtime/pprof"
)

func main() {
    // ========== CPU 性能分析 ==========
    fcpu, err := os.Create("./cpu.pprof")
    if err != nil {
        fmt.Println("创建 cpu.pprof 文件失败:", err.Error())
        return
    }
    defer fcpu.Close()

    // 开始 CPU 性能采样
    err = pprof.StartCPUProfile(fcpu)
    if err == nil {
        defer pprof.StopCPUProfile()
    }

    // 执行需要测试的代码
    var count int
    for i := 0; i < 10000000; i++ {
        count++
    }

    // ========== 内存性能分析 ==========
    fmem, err := os.Create("./memory.pprof")
    if err != nil {
        fmt.Println("创建 memory.pprof 文件失败:", err.Error())
        return
    }
    defer fmem.Close()

    // 写入内存性能数据
    err = pprof.WriteHeapProfile(fmem)
    if err != nil {
        fmt.Println("写入内存性能数据失败:", err.Error())
        return
    }

    fmt.Println("测试完成,count:", count)
}

(二)分析生成的文件

运行上面的代码后,会生成两个文件:

  • cpu.pprof:CPU 性能数据
  • memory.pprof:内存性能数据

使用 go tool pprof 分析这些文件:

# 分析 CPU 性能
go tool pprof -http=:8081 ./cpu.pprof

# 分析内存性能
go tool pprof -http=:8082 ./memory.pprof

(三)实际应用场景

我在项目中经常用这种方式来:

  1. 对比优化前后的性能:生成优化前后的 pprof 文件,对比差异
  2. 压测特定函数:在单元测试中模拟大量调用,找出性能瓶颈
  3. 基准测试(Benchmark):结合 Go 的 benchmark 功能,更精确地测量性能

十、常见性能优化措施

基于我使用 pprof 排查问题的经验,总结了以下几种最常见且最有效的优化方法。

1. 字符串拼接优化

这是我遇到最多的性能问题之一。在循环中使用 += 拼接字符串,性能极差。

错误示例:

var result string
for i := 0; i < 10000; i++ {
    result += "test"  // 每次都会重新分配内存
}

正确做法:

var builder strings.Builder
builder.Grow(40000)  // 预分配容量
for i := 0; i < 10000; i++ {
    builder.WriteString("test")
}
result := builder.String()

性能对比:使用 strings.Builder+=100 倍以上

详细分析可参考:Go语言字符串拼接性能对比与最佳实践 - 深度优化指南

2. 数据库查询优化

在 pprof 中看到数据库相关函数耗时长,通常是慢查询导致。

排查方法:

  • 开启数据库慢查询日志
  • 使用 EXPLAIN 分析查询计划
  • 检查是否缺少索引
  • 避免 SELECT *,只查询需要的字段
  • 批量操作替代单条操作

实际案例: 我曾遇到一个接口响应慢的问题,通过 pprof 发现是一个查询耗时 500ms。添加联合索引后,降到了 5ms,性能提升 100 倍

3. 切片/Map 预分配容量

如果在 pprof 中看到 growslicemapassign 频繁出现,说明切片或 map 在不断扩容。

优化方法:

// 不好的做法
var slice []string
var m map[string]int

// 好的做法:预分配容量
slice := make([]string, 0, expectedSize)
m := make(map[string]int, expectedSize)

效果:减少内存分配次数,降低 GC 压力。

4. 避免协程泄漏

通过 goroutine 分析发现协程数量持续增长,需要及时处理。

最佳实践:

// 使用 context 控制协程生命周期
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

go func(ctx context.Context) {
    select {
    case <-ctx.Done():
        return  // 超时或取消时退出
    case result := <-ch:
        // 处理结果
    }
}(ctx)

5. JSON 序列化性能优化

标准库 encoding/json 在高并发场景下性能不足。

替代方案:

// 使用 json-iterator 替代标准库
import jsoniter "github.com/json-iterator/go"

var json = jsoniter.ConfigCompatibleWithStandardLibrary

// 使用方式和标准库一样
data, err := json.Marshal(obj)

性能提升:比标准库快 2-3 倍,内存分配也更少。

6. 使用对象池(sync.Pool)

对于频繁创建和销毁的对象,使用对象池可以显著降低 GC 压力。

示例:

var bufferPool = sync.Pool{
    New: func() interface{} {
        return new(bytes.Buffer)
    },
}

// 获取对象
buf := bufferPool.Get().(*bytes.Buffer)
buf.Reset()  // 重置状态
defer bufferPool.Put(buf)  // 使用完放回池中

// 使用 buf...

7. 避免反射和类型断言

pprof 中看到大量 reflect 相关调用,说明使用了过多反射。

优化建议

  • 用代码生成替代反射
  • 使用泛型(Go 1.18+)
  • 缓存反射结果

8. 并发控制

无限制地创建 goroutine 会导致资源耗尽。

使用协程池:

// 使用 semaphore 限制并发数
sem := make(chan struct{}, 100)  // 最多 100 个并发

for _, item := range items {
    sem <- struct{}{}  // 获取信号量
    go func(item Item) {
        defer func() { <-sem }()  // 释放信号量
        // 处理任务
    }(item)
}

使用 worker pool: 推荐使用 antstunny 等成熟的协程池库。


以上是我在实际项目中总结的优化经验,具体使用哪种方法,需要根据 pprof 分析结果来决定。记住:先测量,再优化,不要过早优化。

十一、常见问题解答

1. 开启 pprof 会影响服务性能吗?

会有一定影响,但通常可以忽略。

pprof 采用采样机制,对性能的影响通常在 1%-5% 之间。不过在以下情况下影响可能更明显:

  • 高并发场景下长时间采样
  • 频繁采样 CPU 或内存数据
  • 在极度追求性能的场景(如游戏服务器)

我的建议

  • 开发和测试环境可以常驻开启
  • 生产环境按需开启,排查完问题及时关闭
  • 使用配置开关控制 pprof 的启用

2. heap 和 allocs 有什么区别?

这是我经常被问到的问题,两者的区别很关键:

维度 heap(内存堆栈分析) allocs(内存分配分析)
关注点 当前存活的对象 所有分配行为(包含已释放的对象)
数据来源 实时内存快照 内存分配器事件采样
时间维度 空间维度(当前占用) 时间维度(历史累计)
关键指标 inuse_space / inuse_objects alloc_space / alloc_objects
适用场景 内存泄漏 / 常驻内存过大 GC 压力 / 分配热点 / 频繁短命对象
分析重点 “谁一直占着内存不放” “谁在不断申请内存”

快速记忆

  • 查找内存泄漏 → 用 heap
  • 查找频繁分配 → 用 allocs

实战案例: 我曾遇到一个服务,内存使用稳定但 GC 频繁。通过 heap 看内存占用不高,但通过 allocs 发现某个函数在循环中频繁创建临时对象。优化后 GC 次数降低了 80%

3. 如何分析 CPU 耗时?

参见【三、CPU 耗时分析实战】章节。

快速步骤

  1. 执行 go tool pprof -http=:8081 http://0.0.0.0:30552/debug/pprof/profile
  2. 查看火焰图,找"又红又粗"的方框
  3. 在 Top 视图中确认具体代码位置
  4. 针对性优化

4. 如何分析内存占用?

参见【四、内存堆栈分析实战】章节。

要点

  • 查看 heap 火焰图
  • 关注 cum 和 flat 指标
  • 排查是否有全局变量、缓存未清理、goroutine 泄漏

5. 如何分析内存分配?

参见【五、内存分配分析实战】章节。

要点

  • 使用 allocs 火焰图
  • 找出频繁分配的函数
  • 重点关注循环中的内存分配

6. 如何排查协程泄漏?

参见【六、协程分析实战】章节。

排查步骤

  1. 访问 http://0.0.0.0:30552/debug/pprof/goroutine?debug=2
  2. 查看协程总数是否持续增长
  3. 检查堆栈中是否有 channel 阻塞、锁等待、死循环
  4. 为 goroutine 添加退出机制和超时控制

7. pprof 控制台模式常用命令

不使用 -http 参数时,go tool pprof 会进入命令行交互模式。

示例:

$ go tool pprof http://0.0.0.0:30552/debug/pprof/profile
Fetching profile over HTTP from http://0.0.0.0:30552/debug/pprof/profile
Type: cpu
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 150ms, 83.33% of 180ms total
      flat  flat%   sum%        cum   cum%
      30ms 16.67% 16.67%       40ms 22.22%  runtime.lock2
      30ms 16.67% 33.33%       30ms 16.67%  syscall.Syscall
      ...

常用命令

命令 说明
help 查看帮助信息,列出所有支持的命令
top 按指标大小列出前 10 个函数
top 20 列出前 20 个函数
list 函数名 查看指定函数的详细分析(包括源代码)
traces 打印所有调用栈及其指标信息
web 生成 SVG 图形并在浏览器中打开(需要 graphviz)
pdf 生成 PDF 格式的报告
exit / quit 退出交互模式

个人建议: 命令行模式功能强大但不够直观,我更推荐使用 -http 参数启动 web 服务,通过浏览器查看,体验更好。

8. 如何对比优化前后的性能?

方法一:生成两份 pprof 文件对比

# 优化前
go tool pprof -output=before.pprof http://0.0.0.0:30552/debug/pprof/profile

# 优化后
go tool pprof -output=after.pprof http://0.0.0.0:30552/debug/pprof/profile

# 对比两个文件
go tool pprof -http=:8081 -base=before.pprof after.pprof

方法二:使用 benchmark

func BenchmarkOld(b *testing.B) {
    for i := 0; i < b.N; i++ {
        // 优化前的代码
    }
}

func BenchmarkNew(b *testing.B) {
    for i := 0; i < b.N; i++ {
        // 优化后的代码
    }
}

运行:

go test -bench=. -benchmem

这样可以直观地看到性能提升和内存分配的变化。


全文总结

pprof 是 Go 性能优化的利器,掌握它能大幅提升问题排查效率。我的建议是:

  1. 先运行再优化:不要过早优化,用 pprof 找到真正的瓶颈
  2. 多维度分析:CPU、内存、协程要综合看,往往问题相互关联
  3. 持续监控:定期采样分析,及时发现潜在问题
  4. 实践出真知:多在实际项目中使用,积累经验

希望这篇教程能帮助你快速上手 pprof,提升 Go 程序的性能!

版权声明

未经授权,禁止转载本文章。
如需转载请保留原文链接并注明出处。即视为默认获得授权。
未保留原文链接未注明出处或删除链接将视为侵权,必追究法律责任!

本文原文链接: https://fiveyoboy.com/articles/go-pprof/

备用原文链接: https://blog.fiveyoboy.com/articles/go-pprof/