Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Go runtime/pprof 包详解

概述

runtime/pprof 包以 pprof 可视化工具期望的格式写入运行时性能分析数据。

重要说明

  • 提供 CPU、内存、阻塞、互斥锁等多种性能分析
  • 数据格式与 pprof 工具兼容
  • 支持自定义性能分析
  • 可用于生产环境性能调优
  • net/http/pprof 包配合提供 HTTP 接口

支持的 Profile 类型

  • goroutine - 所有当前 goroutine 的堆栈跟踪
  • goroutineleak - 所有泄漏的 goroutine 的堆栈跟踪
  • allocs - 所有过去的内存分配采样
  • heap - 存活对象的内存分配采样
  • threadcreate - 创建新 OS 线程的堆栈跟踪
  • block - 导致阻塞在同步原语上的堆栈跟踪
  • mutex - 竞争互斥锁持有者的堆栈跟踪
  • cpu - CPU 性能分析(通过特殊 API)

包导入

import "runtime/pprof"

基本使用

示例 1:CPU 性能分析

package main

import (
    "flag"
    "log"
    "os"
    "runtime/pprof"
)

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")

func main() {
    flag.Parse()
    
    if *cpuprofile != "" {
        f, err := os.Create(*cpuprofile)
        if err != nil {
            log.Fatal("could not create CPU profile: ", err)
        }
        defer f.Close()
        
        if err := pprof.StartCPUProfile(f); err != nil {
            log.Fatal("could not start CPU profile: ", err)
        }
        defer pprof.StopCPUProfile()
    }
    
    // ... 程序的其余部分 ...
}

运行命令

go test -cpuprofile cpu.prof -bench .
go tool pprof cpu.prof

示例 2:内存性能分析

package main

import (
    "flag"
    "log"
    "os"
    "runtime"
    "runtime/pprof"
)

var memprofile = flag.String("memprofile", "", "write memory profile to `file`")

func main() {
    flag.Parse()
    
    // ... 程序的其余部分 ...
    
    if *memprofile != "" {
        f, err := os.Create(*memprofile)
        if err != nil {
            log.Fatal("could not create memory profile: ", err)
        }
        defer f.Close()
        
        runtime.GC() // 获取最新统计
        
        if err := pprof.WriteHeapProfile(f); err != nil {
            log.Fatal("could not write memory profile: ", err)
        }
    }
}

函数详解(按 a-z 排序)

Do

func Do(ctx context.Context, labels LabelSet, f func(context.Context))

说明:使用添加了给定标签的父上下文副本调用 f。

使用示例

package main

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

func worker(ctx context.Context, id int) {
    // 在带标签的上下文中执行
    pprof.Do(ctx, pprof.Labels("worker", fmt.Sprintf("worker-%d", id)), func(ctx context.Context) {
        // 执行工作
        fmt.Printf("Worker %d running\n", id)
    })
}

func main() {
    ctx := context.Background()
    
    for i := 0; i < 3; i++ {
        go worker(ctx, i)
    }
    
    // 等待完成
    select {}
}

ForLabels

func ForLabels(ctx context.Context, f func(key, value string) bool)

说明:对上下文上的每个标签调用 f。

使用示例

package main

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

func main() {
    ctx := pprof.WithLabels(context.Background(), pprof.Labels("key1", "value1", "key2", "value2"))
    
    pprof.ForLabels(ctx, func(key, value string) bool {
        fmt.Printf("%s = %s\n", key, value)
        return true // 继续迭代
    })
}

运行结果

key1 = value1
key2 = value2

Label

func Label(ctx context.Context, key string) (string, bool)

说明:返回 ctx 上给定键的标签值,以及指示该标签是否存在的布尔值。

使用示例

package main

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

func main() {
    ctx := pprof.WithLabels(context.Background(), pprof.Labels("user", "alice"))
    
    if value, ok := pprof.Label(ctx, "user"); ok {
        fmt.Printf("User: %s\n", value)
    } else {
        fmt.Println("User label not found")
    }
}

运行结果

User: alice

Lookup

func Lookup(name string) *Profile

说明:返回具有给定名称的 profile,如果不存在则返回 nil。

使用示例

package main

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

func main() {
    // 查找 goroutine profile
    p := pprof.Lookup("goroutine")
    if p != nil {
        fmt.Printf("Goroutine profile count: %d\n", p.Count())
        
        // 写入文件
        f, _ := os.Create("goroutines.prof")
        defer f.Close()
        p.WriteTo(f, 0)
    }
    
    // 查找不存在的 profile
    notExist := pprof.Lookup("notexist")
    fmt.Printf("Not exist profile: %v\n", notExist)
}

NewProfile

func NewProfile(name string) *Profile

说明:创建具有给定名称的新 profile。

使用示例

package main

import (
    "fmt"
    "runtime/pprof"
)

// 创建自定义 profile 用于跟踪资源
var dbConnections = pprof.NewProfile("myapp/db_connections")

func openConnection() {
    conn := createConnection()
    dbConnections.Add(conn, 0)
}

func closeConnection(conn interface{}) {
    dbConnections.Remove(conn)
    close(conn)
}

func createConnection() interface{} {
    // 模拟数据库连接
    return &struct{}{}
}

func main() {
    openConnection()
    openConnection()
    
    fmt.Printf("Active connections: %d\n", dbConnections.Count())
}

Profiles

func Profiles() []*Profile

说明:返回所有已知 profile 的切片,按名称排序。

使用示例

package main

import (
    "fmt"
    "runtime/pprof"
)

func main() {
    profiles := pprof.Profiles()
    
    fmt.Printf("Available profiles: %d\n", len(profiles))
    
    for _, p := range profiles {
        fmt.Printf("- %s (%d entries)\n", p.Name(), p.Count())
    }
}

运行结果

Available profiles: 7
- allocs (100 entries)
- block (0 entries)
- goroutine (5 entries)
- heap (50 entries)
- mutex (0 entries)
- threadcreate (1 entries)
- myapp/db_connections (2 entries)

SetGoroutineLabels

func SetGoroutineLabels(ctx context.Context)

说明:将当前 goroutine 的标签设置为与 ctx 匹配。

使用示例

package main

import (
    "context"
    "runtime/pprof"
    "time"
)

func labeledWorker(id int) {
    ctx := pprof.WithLabels(context.Background(), pprof.Labels("worker", "id"))
    pprof.SetGoroutineLabels(ctx)
    
    // 执行工作
    time.Sleep(time.Second)
}

func main() {
    for i := 0; i < 3; i++ {
        go labeledWorker(i)
    }
    
    time.Sleep(2 * time.Second)
}

StartCPUProfile

func StartCPUProfile(w io.Writer) error

说明:为当前进程启用 CPU 性能分析。

使用示例

package main

import (
    "log"
    "os"
    "runtime/pprof"
    "time"
)

func main() {
    f, err := os.Create("cpu.prof")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    
    if err := pprof.StartCPUProfile(f); err != nil {
        log.Fatal(err)
    }
    defer pprof.StopCPUProfile()
    
    // CPU 密集型工作
    sum := 0
    for i := 0; i < 100000000; i++ {
        sum += i
    }
    
    time.Sleep(100 * time.Millisecond) // 等待 profile 写入完成
}

StopCPUProfile

func StopCPUProfile()

说明:停止当前的 CPU 性能分析(如果有的话)。

使用示例:参见 StartCPUProfile 示例。

WithLabels

func WithLabels(ctx context.Context, labels LabelSet) context.Context

说明:返回添加了给定标签的新 context.Context。

使用示例

package main

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

func main() {
    ctx := context.Background()
    
    // 添加标签
    ctx = pprof.WithLabels(ctx, pprof.Labels("request_id", "12345"))
    
    // 添加更多标签(覆盖同名标签)
    ctx = pprof.WithLabels(ctx, pprof.Labels("user", "alice", "request_id", "67890"))
    
    if value, ok := pprof.Label(ctx, "user"); ok {
        fmt.Printf("User: %s\n", value)
    }
    
    if value, ok := pprof.Label(ctx, "request_id"); ok {
        fmt.Printf("Request ID: %s\n", value)
    }
}

运行结果

User: alice
Request ID: 67890

WriteHeapProfile

func WriteHeapProfile(w io.Writer) error

说明:WriteHeapProfile 是 Lookup(“heap”).WriteTo(w, 0) 的简写。

使用示例

package main

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

func main() {
    // 分配一些内存
    data := make([][]byte, 1000)
    for i := range data {
        data[i] = make([]byte, 1024)
    }
    
    f, err := os.Create("heap.prof")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    
    if err := pprof.WriteHeapProfile(f); err != nil {
        log.Fatal(err)
    }
}

类型详解

LabelSet

type LabelSet struct{}

说明:标签集合。

Labels

func Labels(args ...string) LabelSet

说明:接受偶数个字符串作为键值对,并创建包含它们的 LabelSet。

使用示例

package main

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

func processRequest(ctx context.Context, requestID string) {
    labels := pprof.Labels(
        "request_id", requestID,
        "endpoint", "/api/users",
        "method", "GET",
    )
    
    pprof.Do(ctx, labels, func(ctx context.Context) {
        // 处理请求
        fmt.Println("Processing request")
    })
}

func main() {
    ctx := context.Background()
    processRequest(ctx, "req-123")
}

Profile

type Profile struct{}

说明:Profile 是显示导致特定事件实例的调用序列的堆栈跟踪集合。

预定义的 Profile

  • goroutine - 所有当前 goroutine 的堆栈跟踪
  • goroutineleak - 所有泄漏 goroutine 的堆栈跟踪
  • allocs - 所有过去的内存分配采样
  • heap - 存活对象的内存分配采样
  • threadcreate - 创建新 OS 线程的堆栈跟踪
  • block - 导致阻塞在同步原语上的堆栈跟踪
  • mutex - 竞争互斥锁持有者的堆栈跟踪

Add

func (p *Profile) Add(value interface{}, skip int)

说明:将当前执行堆栈添加到 profile,与 value 关联。

使用示例

package main

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

// 自定义 profile 跟踪网络连接
var connections = pprof.NewProfile("myapp/connections")

type Connection struct {
    conn net.Conn
}

func NewConnection(addr string) (*Connection, error) {
    conn, err := net.Dial("tcp", addr)
    if err != nil {
        return nil, err
    }
    
    c := &Connection{conn: conn}
    connections.Add(c, 1) // skip=1 跳过 NewConnection 帧
    
    return c, nil
}

func (c *Connection) Close() error {
    connections.Remove(c)
    return c.conn.Close()
}

func main() {
    conn, err := NewConnection("localhost:8080")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    defer conn.Close()
    
    fmt.Printf("Active connections: %d\n", connections.Count())
}

Count

func (p *Profile) Count() int

说明:返回 profile 中当前执行堆栈的数量。

使用示例

package main

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

func main() {
    goroutineProfile := pprof.Lookup("goroutine")
    
    fmt.Printf("Initial goroutines: %d\n", goroutineProfile.Count())
    
    for i := 0; i < 5; i++ {
        go func() {
            time.Sleep(time.Second)
        }()
    }
    
    time.Sleep(10 * time.Millisecond)
    fmt.Printf("After spawning: %d\n", goroutineProfile.Count())
}

运行结果

Initial goroutines: 1
After spawning: 6

Name

func (p *Profile) Name() string

说明:返回此 profile 的名称。

使用示例

package main

import (
    "fmt"
    "runtime/pprof"
)

func main() {
    profiles := pprof.Profiles()
    
    for _, p := range profiles {
        fmt.Printf("Profile: %s\n", p.Name())
    }
}

Remove

func (p *Profile) Remove(value interface{})

说明:从 profile 中移除与 value 关联的执行堆栈。

使用示例:参见 Add 方法示例。

WriteTo

func (p *Profile) WriteTo(w io.Writer, debug int) error

说明:将 profile 的 pprof 格式快照写入 w。

debug 参数说明

  • debug=0 - 写入 gzip 压缩的协议缓冲区(pprof 工具使用)
  • debug=1 - 写入带注释的旧文本格式
  • debug=2 - 对于 goroutine profile,以 Go 程序 panic 时的格式打印

使用示例

package main

import (
    "os"
    "runtime/pprof"
)

func main() {
    // 二进制格式(用于 pprof 工具)
    f1, _ := os.Create("goroutines_binary.prof")
    defer f1.Close()
    pprof.Lookup("goroutine").WriteTo(f1, 0)
    
    // 文本格式(人类可读)
    f2, _ := os.Create("goroutines_text.prof")
    defer f2.Close()
    pprof.Lookup("goroutine").WriteTo(f2, 1)
    
    // Panic 格式
    f3, _ := os.Create("goroutines_panic.prof")
    defer f3.Close()
    pprof.Lookup("goroutine").WriteTo(f3, 2)
}

典型示例

示例 1:完整的性能分析程序

package main

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

var (
    cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
    memprofile = flag.String("memprofile", "", "write memory profile to `file`")
)

func main() {
    flag.Parse()
    
    // CPU profile
    if *cpuprofile != "" {
        f, err := os.Create(*cpuprofile)
        if err != nil {
            log.Fatal("could not create CPU profile: ", err)
        }
        defer f.Close()
        
        if err := pprof.StartCPUProfile(f); err != nil {
            log.Fatal("could not start CPU profile: ", err)
        }
        defer pprof.StopCPUProfile()
    }
    
    // 执行工作
    work()
    
    // Memory profile
    if *memprofile != "" {
        f, err := os.Create(*memprofile)
        if err != nil {
            log.Fatal("could not create memory profile: ", err)
        }
        defer f.Close()
        
        runtime.GC()
        if err := pprof.WriteHeapProfile(f); err != nil {
            log.Fatal("could not write memory profile: ", err)
        }
    }
}

func work() {
    // CPU 密集型工作
    sum := 0
    for i := 0; i < 100000000; i++ {
        sum += i
    }
    
    // 内存密集型工作
    data := make([][]byte, 1000)
    for i := range data {
        data[i] = make([]byte, 10240)
    }
    
    fmt.Println("Work completed")
}

运行命令

# 运行并生成 profile
go run main.go -cpuprofile cpu.prof -memprofile mem.prof

# 查看 CPU profile
go tool pprof cpu.prof

# 查看 Memory profile
go tool pprof mem.prof

示例 2:HTTP 接口性能分析

package main

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

func main() {
    // 安装 pprof HTTP 处理器
    // 访问 http://localhost:8080/debug/pprof/ 查看 profile
    
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello, World!"))
    })
    
    log.Println("Server starting on :8080")
    log.Println("Visit http://localhost:8080/debug/pprof for profiles")
    
    log.Fatal(http.ListenAndServe(":8080", nil))
}

访问的 URL

  • /debug/pprof/ - Profile 索引页面
  • /debug/pprof/cmdline - 命令行
  • /debug/pprof/profile - CPU profile
  • /debug/pprof/symbol - 符号
  • /debug/pprof/trace - 执行跟踪
  • /debug/pprof/goroutine - Goroutine profile
  • /debug/pprof/heap - Heap profile
  • /debug/pprof/block - Block profile
  • /debug/pprof/mutex - Mutex profile

示例 3:自定义资源跟踪

package main

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

// 自定义 profile 跟踪数据库连接
var dbConnections = pprof.NewProfile("myapp/db_connections")

type DB struct {
    mu       sync.Mutex
    conns    map[int]*Connection
    nextID   int
}

type Connection struct {
    id   int
    db   *DB
    open bool
}

func NewDB() *DB {
    return &DB{
        conns: make(map[int]*Connection),
    }
}

func (db *DB) Open() *Connection {
    db.mu.Lock()
    defer db.mu.Unlock()
    
    conn := &Connection{
        id:   db.nextID,
        db:   db,
        open: true,
    }
    db.nextID++
    db.conns[conn.id] = conn
    
    // 添加到 profile
    dbConnections.Add(conn, 1)
    
    return conn
}

func (c *Connection) Close() error {
    c.db.mu.Lock()
    defer c.db.mu.Unlock()
    
    if !c.open {
        return nil
    }
    
    // 从 profile 移除
    c.db.connections.Remove(c)
    delete(c.db.conns, c.id)
    c.open = false
    
    return nil
}

func (db *DB) Stats() {
    fmt.Printf("Active connections: %d\n", dbConnections.Count())
}

func main() {
    db := NewDB()
    
    // 打开一些连接
    conn1 := db.Open()
    conn2 := db.Open()
    conn3 := db.Open()
    
    db.Stats()
    
    // 关闭一个连接
    conn2.Close()
    
    db.Stats()
    
    // 清理
    conn1.Close()
    conn3.Close()
    
    db.Stats()
}

运行结果

Active connections: 3
Active connections: 2
Active connections: 0

示例 4:带标签的 Goroutine 分析

package main

import (
    "context"
    "fmt"
    "runtime/pprof"
    "time"
)

func worker(ctx context.Context, id int, wg chan struct{}) {
    defer func() { wg <- struct{}{} }()
    
    labels := pprof.Labels(
        "worker_id", fmt.Sprintf("%d", id),
        "task", "processing",
    )
    
    pprof.Do(ctx, labels, func(ctx context.Context) {
        // 模拟工作
        time.Sleep(100 * time.Millisecond)
    })
}

func main() {
    ctx := context.Background()
    wg := make(chan struct{}, 10)
    
    // 启动带标签的 worker
    for i := 0; i < 5; i++ {
        go worker(ctx, i, wg)
    }
    
    // 等待完成
    for i := 0; i < 5; i++ {
        <-wg
    }
    
    // 查看 goroutine profile
    p := pprof.Lookup("goroutine")
    fmt.Printf("Goroutines: %d\n", p.Count())
}

示例 5:阻塞分析

package main

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

func main() {
    // 启用阻塞分析
    runtime.SetBlockProfileRate(1)
    
    var mu sync.Mutex
    var wg sync.WaitGroup
    
    // 制造一些阻塞
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            
            mu.Lock()
            defer mu.Unlock()
            
            // 模拟工作
            time.Sleep(10 * time.Millisecond)
        }(i)
    }
    
    wg.Wait()
    
    // 写入阻塞 profile
    f, err := os.Create("block.prof")
    if err != nil {
        panic(err)
    }
    defer f.Close()
    
    p := pprof.Lookup("block")
    if p != nil {
        p.WriteTo(f, 0)
        fmt.Printf("Block profile entries: %d\n", p.Count())
    }
}

示例 6:互斥锁分析

package main

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

var mu sync.Mutex

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    
    for i := 0; i < 100; i++ {
        mu.Lock()
        // 模拟临界区工作
        time.Sleep(time.Microsecond)
        mu.Unlock()
    }
}

func main() {
    // 启用互斥锁分析
    runtime.SetMutexProfileFraction(1)
    
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }
    wg.Wait()
    
    // 写入互斥锁 profile
    f, err := os.Create("mutex.prof")
    if err != nil {
        panic(err)
    }
    defer f.Close()
    
    p := pprof.Lookup("mutex")
    if p != nil {
        p.WriteTo(f, 0)
        fmt.Printf("Mutex profile entries: %d\n", p.Count())
    }
}

示例 7:分析 Web 服务器

package main

import (
    "flag"
    "fmt"
    "log"
    "net/http"
    "os"
    "runtime/pprof"
    "time"
)

var profile = flag.Bool("profile", false, "enable profiling")

func handler(w http.ResponseWriter, r *http.Request) {
    // 模拟一些工作
    time.Sleep(10 * time.Millisecond)
    fmt.Fprintf(w, "Hello, %s!", r.URL.Path)
}

func main() {
    flag.Parse()
    
    if *profile {
        f, err := os.Create("server.prof")
        if err != nil {
            log.Fatal(err)
        }
        defer f.Close()
        
        if err := pprof.StartCPUProfile(f); err != nil {
            log.Fatal(err)
        }
        defer pprof.StopCPUProfile()
    }
    
    http.HandleFunc("/", handler)
    
    fmt.Println("Server starting on :8080")
    
    // 运行 10 秒
    go func() {
        time.Sleep(10 * time.Second)
        os.Exit(0)
    }()
    
    log.Fatal(http.ListenAndServe(":8080", nil))
}

运行命令

# 启用 profiling 运行
go run main.go -profile

# 在另一个终端发送请求
curl http://localhost:8080/test1
curl http://localhost:8080/test2

# 分析结果
go tool pprof server.prof

示例 8:比较两个 Profile

package main

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

func before() {
    // 优化前的代码
    data := make([]int, 1000000)
    for i := 0; i < len(data); i++ {
        data[i] = i * i
    }
}

func after() {
    // 优化后的代码
    data := make([]int, 1000000)
    for i := range data {
        data[i] = i * i
    }
}

func profile(name string, fn func()) {
    f, _ := os.Create(name)
    defer f.Close()
    
    pprof.StartCPUProfile(f)
    defer pprof.StopCPUProfile()
    
    fn()
    time.Sleep(100 * time.Millisecond)
}

func main() {
    fmt.Println("Profiling before optimization...")
    profile("before.prof", before)
    
    fmt.Println("Profiling after optimization...")
    profile("after.prof", after)
    
    fmt.Println("Compare with: go tool pprof before.prof after.prof")
}

最佳实践

1. 使用 defer 确保停止 Profile

// ✅ 推荐
f, _ := os.Create("cpu.prof")
defer f.Close()
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()

// ❌ 不推荐
f, _ := os.Create("cpu.prof")
pprof.StartCPUProfile(f)
// 可能忘记 StopCPUProfile

2. 在生产环境谨慎使用

// ✅ 推荐:通过 flag 控制
var profile = flag.Bool("profile", false, "enable profiling")

if *profile {
    // 启用 profiling
}

// ❌ 不推荐:始终启用
pprof.StartCPUProfile(f)  // 影响性能

3. 使用 HTTP 接口进行在线分析

// ✅ 推荐:在开发/测试环境
import _ "net/http/pprof"

// 在内部网络使用,不要暴露到公网

4. 自定义 Profile 用于资源跟踪

// ✅ 推荐:跟踪重要资源
var dbConnections = pprof.NewProfile("myapp/db_connections")

// 添加和移除
dbConnections.Add(conn, 1)
dbConnections.Remove(conn)

5. 使用标签改进 Goroutine 分析

// ✅ 推荐:添加有意义的标签
labels := pprof.Labels(
    "request_id", requestID,
    "endpoint", r.URL.Path,
)
pprof.Do(ctx, labels, func(ctx context.Context) {
    // 处理请求
})

与其他包配合

runtime

package main

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

func main() {
    // 设置分析率
    runtime.SetBlockProfileRate(1)
    runtime.SetMutexProfileFraction(1)
    
    // 写入各种 profile
    runtime.GC()
    pprof.WriteHeapProfile(os.Stdout)
}

net/http/pprof

package main

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

func main() {
    // 提供 HTTP 接口
    http.ListenAndServe("localhost:6060", nil)
}

testing

package mypkg

import "testing"

func BenchmarkSomething(b *testing.B) {
    for i := 0; i < b.N; i++ {
        // 测试代码
    }
}

运行命令

go test -cpuprofile cpu.prof -memprofile mem.prof -bench=.

快速参考

函数

函数参数返回值说明
Doctx, labels, f-带标签执行
ForLabelsctx, f-迭代标签
Labelctx, keystring, bool获取标签值
Lookupname string*Profile查找 profile
NewProfilename string*Profile创建 profile
Profiles-[]*Profile获取所有 profile
SetGoroutineLabelsctx-设置 goroutine 标签
StartCPUProfilew io.Writererror开始 CPU profile
StopCPUProfile--停止 CPU profile
WithLabelsctx, labelsContext添加标签到上下文
WriteHeapProfilew io.Writererror写入 heap profile

类型

类型说明
LabelSet标签集合
Profile性能分析集合

Profile 方法

方法返回值说明
Add-添加堆栈
Countint返回条目数
Namestring返回名称
Remove-移除堆栈
WriteToerror写入 profile

预定义 Profile

名称说明
goroutine所有当前 goroutine
goroutineleak泄漏的 goroutine
allocs所有过去的内存分配
heap存活对象的内存分配
threadcreate创建 OS 线程
block阻塞事件
mutex互斥锁竞争

注意事项

1. 性能开销

  • CPU profile 会影响程序性能(约 5-10%)
  • 避免在生产环境长时间启用
  • 使用采样率控制开销

2. 文件处理

  • 始终使用 defer 关闭文件
  • 确保 StopCPUProfile 在 Close 之前调用
  • 检查所有错误返回值

3. 并发安全

  • Profile 方法可并发调用
  • 但要注意数据竞争
  • 使用适当的同步

4. 平台限制

  • 在某些平台上需要特殊权限
  • c-archive/c-shared 模式默认不支持
  • 需要额外的信号处理配置

5. Profile 格式

  • debug=0 用于 pprof 工具
  • debug=1 用于人类阅读
  • debug=2 用于 goroutine panic 格式

总结

runtime/pprof 包提供了 Go 程序性能分析的完整工具集。

核心要点

  1. 使用 StartCPUProfile/StopCPUProfile 进行 CPU 分析
  2. 使用 WriteHeapProfile 进行内存分析
  3. 自定义 Profile 可用于资源跟踪
  4. 标签可以帮助识别 goroutine
  5. HTTP 接口提供在线分析能力

主要用途

  • CPU 性能瓶颈分析
  • 内存泄漏检测
  • Goroutine 泄漏检测
  • 同步原语竞争分析
  • 资源跟踪和调试

工具链

# 生成 profile
go test -cpuprofile cpu.prof -bench=.

# 分析 profile
go tool pprof cpu.prof

# Web 界面
go tool pprof -http=:8080 cpu.prof

# 比较 profile
go tool pprof before.prof after.prof