我是如何在大型代码库上使用 pprof 探索 Go 中的内存泄漏
文章插图
在今年的大部分时间里 , 我一直在 Orbs 团队用 Go 语言做可扩展的区块链的基础设施开发 , 这是令人兴奋的一年 。 在 2018 年的时候 , 我们研究我们的区块链该选择哪种语言实现 。 因为我们知道 Go 拥有一个良好的社区和一个非常棒的工具集 , 所以我们选择了 Go 。
最近几周 , 我们进入了系统整合的最后阶段 。 与任何大型系统一样 , 可能会在后期阶段出现一些问题 , 包括性能问题 , 内存泄漏等 。 当整合系统时 , 我们找到了一个不错的方法 。 在本文中 , 我将介绍如何调查 Go 中的内存泄漏 , 详细说明寻找 , 理解和解决它的步骤 。
Golang 提供的工具集非常出色但也有其局限性 。 首先来看看这个问题 , 最大的一个问题是查询完整的 core dumps 能力有限 。 完整的 core dumps 是程序运行时的进程占用内存(或用户内存)的镜像 。
我们可以把内存映射想象成一棵树 , 遍历那棵树我们会得到不同的对象分配和关系 。 这意味着无论如何 根会持有内存而不被 GCing(垃圾回收)内存的原因 。 因为在 Go 中没有简单的方法来分析完整的 core dump , 所以很难找到一个没有被 GC 过的对象的根 。
在撰写本文时 , 我们无法在网上找到任何可以帮助我们的工具 。 由于存在 core dump 格式以及从 debug 包中导出该文件的简单方法 , 这可能是 Google 使用过的一种方法 。 网上搜索它看起来像是在 Golang pipeline 中创建了这样的 core dump 查看器 , 但看起来并不像有人在使用它 。 话虽如此 , 即使没有这样的解决方案 , 使用现有工具我们通常也可以找到根本原因 。
内存泄漏内存泄漏或内存压力可以以多种形式出现在整个系统中 。 通常我们将它们视为 bug , 但有时它们的根本原因可能是因为设计的问题 。
当我们在新的设计原则下构建我们的系统时 , 这些考虑并不重要 。 更重要的是以避免过早优化的方式构建系统 , 并使你能够在代码成熟后再优化它们 , 而不是从一开始就过度设计它 。 然而 , 一些常见内存压力的问题是:
- 内存分配太多 , 数据表示不正确
- 大量使用反射或字符串
- 使用全局变量
- 孤儿 , 没有结束的 Goroutines
我为什么要写这篇文章呢?当我研究这个例子时 , 我发现了很多关于内存泄漏的方法 。 但是 , 相比较这个例子 , 我们的真实系统有超过 50 行代码和单个结构 。 在这种情况下 , 找到内存问题的来源比该示例描述的要复杂得多 。
Golang 为我们提供了一个神奇的工具叫 pprof 。 掌握此工具后 , 可以帮助调查并发现最有可能的内存问题 。 它的另一个用途是查找 CPU 问题 , 但我不会在这篇文章中介绍任何与 CPU 有关的内容 。
go tool pprof把这个工具的方方面面讲清楚需要不止一篇博客文章 。 我将花一点时间找出怎么使用这个工具去获取有用的东西 。 在这篇文章里 , 将集中在它的内存相关功能上 。
pprof 包创建一个 heap dump 文件 , 你可以在随后进行分析 / 可视化以下两种内存映射:
- 当前的内存分配
- 总(累积)内存分配
pprof 画像pprof 的工作方式是使用画像 。
画像 (profile) 是一组显示导致特定事件实例的调用序列的堆栈追踪 , 例如内存分配 。
文件runtime/pprof/pprof.go[2] 包含画像的详细信息和实现 。
Go 有几个内置的画像供我们在常见情况下使用:
- Goroutine - 当前所有 Goroutines 的堆栈跟踪
- heap - 当前存活对象的内存分配的采样
- allocs - 过去所有内存分配的采样
- threadcreate - 导致创建新 OS 线程的堆栈跟踪信息
- block - 导致同步原语阻塞的堆栈跟踪信息
- mutex - 锁争用的持有者的堆栈跟踪信息
- 页面|如何简单、快速制作流程图?上班族的画图技巧get
- 培育|跨境电商人才如何培育,长沙有“谱”了
- 抖音小店|抖音进军电商,短视频的商业模式与变现,创业者该如何抓住机遇?
- 计费|5G是如何计费的?
- 车轮旋转|牵引力控制系统是如何工作的?它有什么作用?
- 视频|短视频如何在前3秒吸引用户眼球?
- Vlog|中国Vlog|中国基建如何升级?看5G+智慧工地
- 涡轮|看法米特涡轮流量计如何让你得心应手
- 手机|OPPO手机该如何截屏?四种最简单的方法已汇总!
- 和谐|人民日报海外版今日聚焦云南西双版纳 看科技如何助力人象和谐