40分钟学 Go 语言高并发:pprof性能分析工具详解

server/2024/12/5 17:10:18/

pprof性能分析工具详解

一、知识要点概述

分析类型主要功能使用场景重要程度
CPU分析分析CPU使用情况和热点函数性能优化、CPU密集型任务分析⭐⭐⭐⭐⭐
内存分析分析内存分配和泄漏问题内存优化、泄漏排查⭐⭐⭐⭐⭐
协程分析分析goroutine的创建和阻塞并发问题排查、死锁分析⭐⭐⭐⭐
性能火焰图直观展示CPU和内存使用情况性能瓶颈可视化分析⭐⭐⭐⭐
package mainimport ("fmt""log""net/http"_ "net/http/pprof"  // 引入pprof"runtime""sync""time"
)// 模拟CPU密集型操作
func cpuIntensiveTask() {for i := 0; i < 1000000; i++ {_ = fmt.Sprintf("number: %d", i)}
}// 模拟内存分配
func memoryAllocationTask() {var memoryLeakSlice []stringfor i := 0; i < 100000; i++ {memoryLeakSlice = append(memoryLeakSlice, fmt.Sprintf("memory leak string: %d", i))if i%100 == 0 {time.Sleep(1 * time.Millisecond)}}
}// 模拟goroutine泄露
func goroutineLeakTask() {for i := 0; i < 100; i++ {go func() {// 永远阻塞的goroutineselect {}}()}
}// 模拟锁竞争
func lockContentionTask() {var mutex sync.Mutexvar wg sync.WaitGroupfor i := 0; i < 10; i++ {wg.Add(1)go func() {defer wg.Done()for j := 0; j < 1000; j++ {mutex.Lock()time.Sleep(100 * time.Microsecond)mutex.Unlock()}}()}wg.Wait()
}func startProfileServer() {// 启动pprof服务器go func() {log.Println(http.ListenAndServe("localhost:6060", nil))}()
}func printMemStats() {var ms runtime.MemStatsruntime.ReadMemStats(&ms)fmt.Printf("Alloc: %d MB, TotalAlloc: %d MB, Sys: %d MB, NumGC: %d\n",ms.Alloc/1024/1024,ms.TotalAlloc/1024/1024,ms.Sys/1024/1024,ms.NumGC)
}func main() {// 启动profile服务器startProfileServer()fmt.Println("Profile server started at http://localhost:6060/debug/pprof/")// 无限循环执行任务for {fmt.Println("\n执行性能测试任务...")// CPU密集型任务fmt.Println("执行CPU密集型任务")cpuIntensiveTask()// 内存分配任务fmt.Println("执行内存分配任务")memoryAllocationTask()printMemStats()// Goroutine泄露任务fmt.Println("执行goroutine泄露任务")goroutineLeakTask()fmt.Printf("当前goroutine数量: %d\n", runtime.NumGoroutine())// 锁竞争任务fmt.Println("执行锁竞争任务")lockContentionTask()time.Sleep(2 * time.Second)}
}

二、pprof使用详解

1. 启用pprof

pprof可以通过以下两种方式启用:

  1. HTTP服务方式:
import _ "net/http/pprof"
go func() {log.Println(http.ListenAndServe("localhost:6060", nil))
}()
  1. 手动生成profile文件:
import "runtime/pprof"
// CPU profile
f, err := os.Create("cpu.prof")
if err != nil {log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()

2. CPU分析详解

CPU profile的主要命令:

# 收集30秒的CPU profile
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30# 分析CPU profile文件
go tool pprof cpu.prof

常用的pprof交互命令:

命令说明
top显示最耗CPU的函数
list functionName显示函数的代码和耗时
web在浏览器中查看调用图
traces显示调用追踪
package mainimport ("flag""log""os""runtime/pprof""time"
)var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")func timeConsumingFunction(duration time.Duration) {start := time.Now()for time.Since(start) < duration {for i := 0; i < 1000000; i++ {_ = i * i}}
}func main() {flag.Parse()if *cpuprofile != "" {f, err := os.Create(*cpuprofile)if err != nil {log.Fatal(err)}if err := pprof.StartCPUProfile(f); err != nil {log.Fatal(err)}defer pprof.StopCPUProfile()}// 执行一些CPU密集型操作for i := 0; i < 5; i++ {timeConsumingFunction(1 * time.Second)}
}

3. 内存分析详解

内存分析主要关注以下指标:

  1. Alloc:当前堆上分配的内存
  2. TotalAlloc:累计分配的内存
  3. Sys:从系统获取的内存
  4. HeapObjects:堆对象数量

收集内存profile:

# 查看堆内存分配情况
go tool pprof http://localhost:6060/debug/pprof/heap# 查看内存分配位置
go tool pprof -alloc_space http://localhost:6060/debug/pprof/heap
package mainimport ("flag""fmt""log""os""runtime""runtime/pprof"
)var memprofile = flag.String("memprofile", "", "write memory profile to file")// 模拟内存泄露的结构体
type MemoryLeak struct {data []byte
}var leaks []*MemoryLeakfunc allocateMemory() {// 分配大量内存但不释放for i := 0; i < 1000; i++ {leak := &MemoryLeak{data: make([]byte, 1024*1024), // 1MB}leaks = append(leaks, leak)}
}func printMemStats(msg string) {var m runtime.MemStatsruntime.ReadMemStats(&m)fmt.Printf("%s:\n", msg)fmt.Printf("Alloc = %v MB\n", m.Alloc/1024/1024)fmt.Printf("TotalAlloc = %v MB\n", m.TotalAlloc/1024/1024)fmt.Printf("Sys = %v MB\n", m.Sys/1024/1024)fmt.Printf("NumGC = %v\n\n", m.NumGC)
}func main() {flag.Parse()// 打印初始内存状态printMemStats("初始状态")// 分配内存allocateMemory()// 打印分配后的内存状态printMemStats("分配内存后")// 手动触发GCruntime.GC()printMemStats("GC后")// 写入内存profileif *memprofile != "" {f, err := os.Create(*memprofile)if err != nil {log.Fatal(err)}if err := pprof.WriteHeapProfile(f); err != nil {log.Fatal(err)}f.Close()}
}

4. 协程分析详解

协程分析主要关注:

  1. goroutine数量
  2. goroutine状态
  3. 阻塞和死锁情况

收集goroutine信息:

# 查看goroutine堆栈
go tool pprof http://localhost:6060/debug/pprof/goroutine# 查看阻塞分析
go tool pprof http://localhost:6060/debug/pprof/block
package mainimport ("fmt""net/http"_ "net/http/pprof""runtime""sync""time"
)func monitorGoroutines() {for {fmt.Printf("当前goroutine数量: %d\n", runtime.NumGoroutine())time.Sleep(time.Second)}
}// 模拟goroutine泄露
func leakyGoroutine(wg *sync.WaitGroup) {defer wg.Done()ch := make(chan int)go func() {// 这个goroutine将永远阻塞<-ch}()
}// 模拟死锁情况
func deadlockSimulation(wg *sync.WaitGroup) {defer wg.Done()var mu1, mu2 sync.Mutex// Goroutine 1go func() {for i := 0; i < 100; i++ {mu1.Lock()time.Sleep(100 * time.Millisecond)mu2.Lock()mu2.Unlock()mu1.Unlock()}}()// Goroutine 2go func() {for i := 0; i < 100; i++ {mu2.Lock()time.Sleep(100 * time.Millisecond)mu1.Lock()mu1.Unlock()mu2.Unlock()}}()
}func main() {// 启动pprofgo func() {fmt.Println("启动pprof服务器在 :6060")fmt.Println(http.ListenAndServe("localhost:6060", nil))}()// 启动goroutine监控go monitorGoroutines()var wg sync.WaitGroup// 创建一些泄露的goroutinesfmt.Println("创建泄露的goroutines...")for i := 0; i < 10; i++ {wg.Add(1)go leakyGoroutine(&wg)}// 模拟死锁情况fmt.Println("模拟死锁情况...")wg.Add(1)go deadlockSimulation(&wg)// 等待所有goroutine完成wg.Wait()// 保持程序运行以便查看pprofselect {}
}

5. 性能火焰图

火焰图分析方法:

  • x轴:代表采样的时间区间
  • y轴:代表调用栈的深度
  • 每一块的宽度:代表该函数在采样时间内的执行时间占比
package mainimport ("fmt""log""net/http"_ "net/http/pprof""os""runtime/pprof""sync""time"
)// 模拟不同的计算密集型任务
type Task struct {name stringfn   func()
}// CPU密集型计算
func computeIntensive() {for i := 0; i < 1000000; i++ {_ = fmt.Sprintf("number: %d", i)}
}// 递归计算斐波那契数列
func fibonacci(n int) int {if n <= 1 {return n}return fibonacci(n-1) + fibonacci(n-2)
}// 模拟IO操作
func simulateIO() {time.Sleep(100 * time.Millisecond)
}// 并发任务处理
func processConcurrentTasks(tasks []Task, workers int) {var wg sync.WaitGrouptaskCh := make(chan Task, len(tasks))// 启动工作协程for i := 0; i < workers; i++ {wg.Add(1)go func(workerID int) {defer wg.Done()for task := range taskCh {fmt.Printf("Worker %d processing task: %s\n", workerID, task.name)task.fn()}}(i)}// 分发任务for _, task := range tasks {taskCh <- task}close(taskCh)wg.Wait()
}func main() {// 启动pprof http服务go func() {log.Println(http.ListenAndServe("localhost:6060", nil))}()// 创建CPU profile文件f, err := os.Create("cpu_profile.prof")if err != nil {log.Fatal(err)}defer f.Close()if err := pprof.StartCPUProfile(f); err != nil {log.Fatal(err)}defer pprof.StopCPUProfile()// 准备任务列表tasks := []Task{{"计算密集型任务", computeIntensive},{"斐波那契计算", func() { fibonacci(35) }},{"IO模拟", simulateIO},}// 执行多轮测试for round := 1; round <= 3; round++ {fmt.Printf("\n=== 执行第 %d 轮测试 ===\n", round)processConcurrentTasks(tasks, 3)time.Sleep(500 * time.Millisecond)}// 创建内存profilememFile, err := os.Create("mem_profile.prof")if err != nil {log.Fatal(err)}defer memFile.Close()if err := pprof.WriteHeapProfile(memFile); err != nil {log.Fatal(err)}fmt.Println("\n性能分析完成。使用以下命令查看结果:")fmt.Println("go tool pprof -http=:8080 cpu_profile.prof")fmt.Println("go tool pprof -http=:8080 mem_profile.prof")
}

6. 性能分析流程图

在这里插入图片描述

7. 常见性能问题及解决方案

  1. CPU密集型问题

    • 症状:CPU使用率高,但吞吐量低
    • 分析方法:
      go tool pprof -http=:8080 cpu_profile.prof
      
    • 常见解决方案:
      • 优化算法复杂度
      • 使用并发处理
      • 减少内存分配
  2. 内存问题

    • 症状:内存使用持续增长,GC频繁
    • 分析方法:
      go tool pprof -http=:8080 mem_profile.prof
      
    • 常见解决方案:
      • 使用对象池
      • 减少临时对象创建
      • 及时释放不用的资源
  3. Goroutine泄露

    • 症状:Goroutine数量持续增长
    • 分析方法:
      go tool pprof -http=:8080 goroutine_profile.prof
      
    • 解决方案:
      • 使用context控制生命周期
      • 合理设置超时机制
      • 正确关闭channel

8. 性能优化最佳实践

  1. 基准测试
func BenchmarkFunction(b *testing.B) {for i := 0; i < b.N; i++ {// 待测试的函数}
}
  1. 持续监控
  • 设置性能指标基线
  • 定期收集性能数据
  • 对比分析性能变化
  1. 优化策略
  • 先优化瓶颈
  • 考虑成本收益比
  • 保持代码可维护性

9. 性能分析工具清单

工具用途使用场景
go tool pprofCPU和内存分析性能优化、内存泄露
go tool trace并发和阻塞分析并发问题、死锁分析
go test -bench基准测试性能对比、优化验证
go vet代码静态分析发现潜在问题

10. 总结

掌握pprof性能分析工具的要点:

  1. 基础知识

    • 理解不同类型的profile
    • 熟悉采样机制和原理
    • 掌握数据分析方法
  2. 实践技能

    • 会使用各种分析工具
    • 能读懂性能数据
    • 掌握优化技巧
  3. 注意事项

    • 生产环境谨慎开启
    • 合理设置采样参数
    • 注意性能影响
  4. 进阶方向

    • 深入理解GC机制
    • 掌握协程调度原理
    • 了解系统监控方案

怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!


http://www.ppmy.cn/server/146262.html

相关文章

使用Apache HttpClient发起一个GET HTTP请求

Apache HttpClient 是一个强大且灵活的Java库&#xff0c;用于处理HTTP请求。 它提供了广泛的功能&#xff0c;包括对不同HTTP方法的支持、连接管理、Cookie处理等。 无论是与RESTful API交互、下载网页内容还是自动化网页任务&#xff0c;Apache HttpClient 都能通过其简洁而…

HarmonyOS:应用沙箱

一、应用文件概述 应用文件&#xff1a;文件所有者为应用&#xff0c;包括应用安装文件、应用资源文件、应用缓存文件等。 设备上应用所使用及存储的数据&#xff0c;以文件、键值对、数据库等形式保存在一个应用专属的目录内。该专属目录我们称为“应用文件目录”&#xff0c;…

Mybatis Plus 增删改查方法(一、增)

先定义一个简单的测试表&#xff0c;执行脚本如下&#xff1a; create table user(id bigint primary key auto_increment,name varchar(255) not null,age int not null default 0 check (age > 0) ); 根据Spingbootmybatisplus的结构根据表自行构建结构&#xff0c;大致…

MySQL中的锁与MVCC

目录 锁 共享锁&#xff08;Shared Locks&#xff09; 排他锁&#xff08;Exclusive Locks&#xff09; 意向锁&#xff08;Intention Locks&#xff09; 记录锁&#xff08;Record Locks&#xff09; 临键锁&#xff08;Next-Key Locks&#xff09; MVCC机制 MVCC的核心…

【线上问题记录 | 排查网络连接问题】

问题描述 现在有我们程序是部署在服务器A的&#xff0c;A链接的是B。程序从B的redis进行存储和取数据的。 我们的业务是: 信息展示&#xff0c;也就是如果发现机器有异常了&#xff0c;实时进行监控。突然发现有一天&#xff0c;信息显示延迟了。 然后我们就开始排查究竟什么原…

在xml的sql的子查询中使用row_number over之后再在mapper的接口层传入Page对象实现分页功能,出现Bug

1.报错信息复现&#xff1a; Mapper接口&#xff1a; List<UserInfo> queryUserPage(Param(“vo”) UserQury query,Page<UserInfo> page); UserQury 类中的状态字段&#xff1a; ApiModelproperty(“状态”) private String status; Xml中sql如下&#xff1…

详解SpringCloud集成Camunda7.19实现工作流审批(二)

本章将分享的是camunda流程设计器--Camunda Modeler的基本使用&#xff08;对应camunda版本是7.19&#xff09;&#xff0c;包括bpmn流程图画法&#xff0c;各种控件使用以及一些日常业务场景的流程图的实现 参考资料&#xff1a; Camunda BPMN 基础组件-CSDN博客 Camunda: Exe…

【PX4_Autopolite飞控源码】中飞控板初始化过程中的引脚IO控制(拉低/拉高)

先选择自己飞控板支持的硬件平台 打开对应的路径我的是Desktop/px4/PX4-Autopilot/boards/zhty/nora 找到board_config.h文件&#xff0c;打开nora后再往下去找Desktop/px4/PX4-Autopilot/boards/zhty/nora/src/borad_config.h 打开后可以看到有很多GPIO引脚的定义&#xff0c…