您现在的位置是:亿华云 > IT科技
生产环境Go程序内存泄露,用pprof如何快速定位
亿华云2025-10-03 20:20:54【IT科技】9人已围观
简介内存泄漏可以在整个系统中以多种形式出现,除了在写代码上的疏忽,忘了关闭该关闭的资源外,更多的时候导致系统发生内存泄露原因可能是设计上决策不对、或者业务逻辑上的疏忽没有考虑到一些边界条件。比如查数据库时
内存泄漏可以在整个系统中以多种形式出现,生产速定除了在写代码上的环境何快疏忽,忘了关闭该关闭的程序资源外,更多的内存时候导致系统发生内存泄露原因可能是设计上决策不对、或者业务逻辑上的泄露疏忽没有考虑到一些边界条件。
比如查数据库时,生产速定有个查询条件在一定情况下应用不到,环境何快导致程序被迫持有一个超大的程序结果集,这样持续一段时间,内存执行相同任务的泄露线程一多,就会造成内存泄露。生产速定
Golang 为我们提供了 pprof 工具。环境何快掌握之后,程序可以帮助排查程序的内存内存泄露问题,当然除了排查内存,泄露它也能排查 CPU 占用过高,线程死锁的这些问题,不过这篇文章我们会聚焦在怎么用 pprof 排查程序的内存泄露问题。
Go 开发的系统中,怎么 添加 pprof 进行采样的步骤,在这里我就不再细说了,因为我之前的云南idc服务商文章,对 pprof 的安装和使用做了详细的说明,文章链接我放在这里:
pprof工具集,提供了Go程序内部多种性能指标的采样能力,我们常会用到的性能采样指标有这些:
profile:CPU采样heap:堆中活跃对象的内存分配情况的采样goroutine:当前所有goroutine的堆栈信息allocs: 会采样自程序启动所有对象的内存分配信息(包括已经被GC回收的内存)threadcreate:采样导致创建新系统线程的堆栈信息上面 heap 和 allocs 是两个与内存相关的指标, allocs 指标会采样自程序启动所有对象的内存分配信息。一般是在想要分析哪些代码能优化提高效率时,查看的指标。针对查看内存泄露问题的分析,网站模板使用则的是 heap 指标里的采样信息。
Heappprof 的 heap 信息,是对堆中活跃对象的内存分配情况的采样。Go 里边哪些对象会被分配到堆上?一般概况就是,被多个函数引用的对象、全局变量、超过一定体积(32KB)的对象都会被分配到堆上,当然对于 Go 来说还会有其他的一些情况会让对象逃逸到堆上。
具体哪些变量会被分配到堆上、以及内存逃逸的事儿,就不多说了,想看详细情况的,看下面这两篇文章。
图解Go内存管理器的内存分配策略Go内存管理之代码的逃逸分析Heap 采样要使用 pprof 获取 heap 指标的采样信息,一种情况是使用 "net/http/pprof" 包
import (
"net/http/pprof"
)
func main() {
http.HandleFunc("/debug/pprof/heap", pprof.Index)
......
http.ListenAndServe(":80", nil)
}然后通过 HTTP 请求的方式获得
curl -sK -v https://example.com/debug/pprof/profile > heap.out还有一种主要的服务器托管方法是使用runtime.pprof 提供的方法,把采样信息保存到文件。
pprof.Lookup("heap").WriteTo(profile_file, 0)关于这两个包的使用方式,以及怎么把信息采样到文件,上面介绍自动采样的文章里有详细的介绍,这里就不再花过多篇幅了。
下面进入文章的正题, 拿到采样文件后,怎么用 pprof 排查出代码哪里导致了内存泄露。
用 pprof 找出内存泄露的地方
pprof 在采样 heap 指标的信息时,使用的是 runtime.MemProfile 函数,该函数默认收集每个 512KB 已分配字节的分配信息。我们可以设置让 runtime.MemProfile 收集所有对象的信息,不过这会对程序的性能造成影响。
当我们拿到采样文件后,就可以通过 go tool pprof 将信息加载到一个交互模式的控制台中。
> go tool pprof heap.out进入,交互式控制台后,一般会有如下的提示:
File: heap.out
Type: inuse_space
Time: Feb 1, 2022 at 10:11am (CST)
Entering interactive mode (type "help" for commands, "o" for options)这里的 Type: inuse_space 指明了文件内采样信息的类型, Type 可能的值有:
inuse_space — 已分配但尚未释放的内存空间inuse_objects——已分配但尚未释放的对象数量alloc_space — 分配的内存总量(已释放的也会统计)alloc_objects — 分配的对象总数(无论是否释放)接下来,介绍一个 pprof 交互式模式下的命令 top,也可以是 topN,比如 top10。这个跟Linux 系统的 top 命令类似,输出 Top N 个最占用内存的函数。
(pprof) top10
Showing nodes accounting for 134.55MB, 92.16% of 145.99MB total
Dropped 60 nodes (cum <= 0.73MB)
Showing top 10 nodes out of 117
flat flat% sum% cum cum%
60.53MB 41.46% 41.46% 85.68MB 58.69% github.com/jinzhu/gorm.glob..func2
18.65MB 12.77% 54.24% 18.65MB 12.77% regexp.(*Regexp).Split
16.95MB 11.61% 65.84% 16.95MB 11.61% github.com/jinzhu/gorm.(*Scope).AddToVars
8.67MB 5.94% 71.78% 129.05MB 88.39% example.com/xxservice/dummy.GetLargeData
7.50MB 5.14% 82.63% 7.50MB 5.14% reflect.packEface
6.50MB 4.45% 87.08% 6.50MB 4.45% fmt.Sprintf
4MB 2.74% 89.82% 4MB 2.74% runtime.malg
1.91MB 1.31% 91.13% 1.91MB 1.31% strings.Replace
1.51MB 1.03% 92.16% 1.51MB 1.03% bytes.makeSlice在这两个里边,最占用内存的前三是 gorm 库的一个方法,gorm 是个 ORM 库,但是导致它内存泄露的原因应该是后面一个有业务逻辑的代码,dummy.GetLargeData 方法。
在 top 指令输出的列表中,我们可以看到两个值,flat 和 cum。
flat:表示此函数分配、并由该函数持有的内存空间。cum:表示由这个函数或它调用堆栈下面的函数分配的内存总量。此外 sum % 表示前面几行输出的 flat百分比之和, 比如上面第四行 sum% 列的值是, 71.78% 实际上就是它以及它上面三行输出的 flat% 的总和。
定位到导致内存泄露的函数后,后面要做的优化问题就是,深入函数内部,看哪里使用不当或者有逻辑上的疏忽,比如我开头举得那个查询条件在有些情况下应用不上的例子。
当然如果你想在函数内部再精确的定位到底是哪段代码导致的内存溢出,也是有办法的,这时候就需要用到 list 指令了。
list 指令可以列出函数内部,每一行代码运行时分配的内存(如果分析CPU的采样文件,则会显示CPU使用时间)
(pprof) list dummy.GetLargeData
Total: 814.62MB
ROUTINE ======================== dummy.GetLargeData in /home/xxx/xxx/xxx.go
814.62MB 814.62MB (flat, cum) 100% of Total
. . 20: }()
. . 21:
. . 22: tick := time.Tick(time.Second / 100)
. . 23: var buf []byte
. . 24: for range tick {
814.62MB 814.62MB 25: buf = append(buf, make([]byte, 1024*1024)...)
. . 26: }
. . 27:}
. . 28:总结
这里把用 pprof 怎么排查程序的内存泄露做了个简单的总结,当然如果你们公司有条件上持续采样,或者我之前文章说的自动采样方案的话,最好还是用上,让机器帮我们做这些事情。
不过不管是用什么办法,最终只能是帮我们定位出来哪里造成了内存泄露,至于要怎么优化解决这个问题,还得具体情况具体分析,如果是一些业务逻辑实现上的问题,那就得跟团队商量一下实现方式,可能还会涉及到产品上的一些改动。
很赞哦!(8661)
下一篇: 数字孪生为数据中心插上“可持续”的翅膀
相关文章
- 液冷解决方案备受关注! 2023年数据中心热点技术盘点
- 第六:这个圈子里的域名确实是赚钱的一些大玩家,至于小米农,有多少赚钱?几乎没有,也就是说,轿子里只有一个人,而且大多数人都抬着轿子。
- 为什么说注册域名注意细节?哪些我们不能忽视?
- 打开https://www.aizhan.com/输入自己想要查询的域名然后按回车键,如果做过网站都会有数据显示出来
- 如何保持会话粘性,看看 Nginx 怎么做的
- 互联网中的地址是数字的IP地址,域名解析的作用主要就是为了便于记忆。
- 2、根据用户基础选择访问提供程序。由于互联问题的存在,接入商的选择也非常重要,如果用户群主要在联通,尽量选择联通接入较好的接入商,如果用户群主要在电信,那么选择电信接入较好的接入商。如果用户组位于国家/地区,则选择更好的访问提供程序进行交互。
- 解析之后一般在十分钟内生效,如果没有生效可以联系域名服务商进行沟通。
- 什么是数据中心的交叉连接?
- 审核通过的域名将显示在域名竞拍页面,并进入正式拍卖期,买家可以在拍卖周期内出价,加价幅度与拍卖保证金说明,点此查看。
热门文章
- 戴尔科技第二财季营收创历史新高
- 域名资源有限,好域名更是有限,但机会随时都有,这取决于我们能否抓住机会。一般观点认为,国内域名注册太深,建议优先考虑外国注册人。外国注册人相对诚实,但价格差别很大,从几美元到几十美元不等。域名投资者应抓住机遇,尽早注册国外域名。
- 2、根据用户基础选择访问提供程序。由于互联问题的存在,接入商的选择也非常重要,如果用户群主要在联通,尽量选择联通接入较好的接入商,如果用户群主要在电信,那么选择电信接入较好的接入商。如果用户组位于国家/地区,则选择更好的访问提供程序进行交互。
- .com域名是国际最广泛流行的通用域名,目前全球注册量第一的域名,公司企业注册域名的首选。国际化公司通常会注册该类域名。
站长推荐
戴尔Precision 3470有效提升SOLIDWORKS设计者工作效率
为什么起域名意义非凡?起域名有什么名堂?
互联网其实拼的也是人脉,域名投资也是一个时效性很强的东西,一个不起眼的消息就会引起整个域名投资市场的动荡,因此拓宽自己的人脉圈,完善自己的信息获取渠道,让自己能够掌握更为多样化的信息,这样才更有助于自己的域名投资。
为啥修改dns服务器?dns服务器与域名有何联系?
数据中心如何将其电力基础设施用于电网运营
最后提醒我们,域名到期后要及时更新域名,否则可能会丢掉域名,每次抢先注册都不会成功。
为什么起域名意义非凡?起域名有什么名堂?
公司名字不但要与其经营理念、活动识别相统一,还要能反映公司理念,服务宗旨、商品形象,从而才能使人看到或听到公司的名称就能产生愉快的联想,对商店产生好感。这样有助于公司树立良好的形象。