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=.
快速参考
函数
| 函数 | 参数 | 返回值 | 说明 |
|---|---|---|---|
| Do | ctx, labels, f | - | 带标签执行 |
| ForLabels | ctx, f | - | 迭代标签 |
| Label | ctx, key | string, bool | 获取标签值 |
| Lookup | name string | *Profile | 查找 profile |
| NewProfile | name string | *Profile | 创建 profile |
| Profiles | - | []*Profile | 获取所有 profile |
| SetGoroutineLabels | ctx | - | 设置 goroutine 标签 |
| StartCPUProfile | w io.Writer | error | 开始 CPU profile |
| StopCPUProfile | - | - | 停止 CPU profile |
| WithLabels | ctx, labels | Context | 添加标签到上下文 |
| WriteHeapProfile | w io.Writer | error | 写入 heap profile |
类型
| 类型 | 说明 |
|---|---|
| LabelSet | 标签集合 |
| Profile | 性能分析集合 |
Profile 方法
| 方法 | 返回值 | 说明 |
|---|---|---|
| Add | - | 添加堆栈 |
| Count | int | 返回条目数 |
| Name | string | 返回名称 |
| Remove | - | 移除堆栈 |
| WriteTo | error | 写入 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 程序性能分析的完整工具集。
核心要点:
- 使用 StartCPUProfile/StopCPUProfile 进行 CPU 分析
- 使用 WriteHeapProfile 进行内存分析
- 自定义 Profile 可用于资源跟踪
- 标签可以帮助识别 goroutine
- 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