从真实事故出发:golang 内存问题排查指北

本文从一起真实发生的内存报警事件出发,详细介绍了排查Go语言内存问题的过程,包括对goroutine逃逸、代码内存泄露和RSS问题的分析。通过pprof工具发现,问题根源在于Go 1.15的runtime GC策略与特定内核版本的不匹配,导致RSS内存占用过高。解决方案是升级Go编译器版本至1.16以上,避免内存不及时释放的问题。此外,文中还探讨了Go中常见的内存泄露场景,如goroutine阻塞、select使用不当和channel管理不当等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题出现


出现报警!!!

在日常搬砖的某一天发现了某微服务 bytedance.xiaoming 服务有一些实例内存过高,达到 80%。而这个服务很久没有上线过新版本,所以可以排除新代码上线引入的问题。

390c66128ce72e5153f6420b287d4238.png

发现问题后,首先进行了迁移实例,除一台实例留作问题排查外,其余实例进行了迁移,迁移过后新实例内存较低。但发现随着时间推移,迁移过的实例内存也有缓慢增高的现象,有内存泄漏的表现。

问题定位


推测一:怀疑是 goroutine 逃逸

排查过程

通常内存泄露的主因就是 goroutine 过多,因此首先怀疑 goroutine 是否有问题,去看了 goroutine 发现很正常,总量较低且没有持续增长现象。(当时忘记截图了,后来补了一张图,但是 goroutine 数量一直是没有变化的)

ce96f2c1f74bfe6c20f160f8c5ab0b92.png

排查结果

没有 goroutine 逃逸问题。

推测二:怀疑代码出现了内存泄露

排查过程

通过 pprof 进行实时内存采集,对比问题实例和正常实例的内存使用状况:

问题实例:

4f4ca0ce7f5aeea8df879f6e1a92d5d8.png

正常实例:

4a9476904d15e4e787058a53dbfd43f1.png

进一步看问题实例的 graph:

476e24e854bbd34d6ed5557f80ca4077.png

从中可以发现,metircs.flushClients()占用的内存是最多的,去定位源码:

func (c *tagCache) Set(key []byte, tt *cachedTags) {

if atomic.AddUint64(&c.setn, 1)&0x3fff == 0 {

// every 0x3fff times call, we clear the map for memory leak issue

// there is no reason to have so many tags

// FIXME: sync.Map don’t have Len method and setn may not equal to the len in concurrency env

samples := make([]interface{}, 0, 3)

c.m.Range(func(key interface{}, value interface{}) bool {

c.m.Delete(key)

if len(samples) < cap(samples) {

samples = append(samples, key)

}

return true

}) // clear map

logfunc(“[ERROR] gopkg/metrics: too many tags. samples: %v”, samples)

}

c.m.Store(string(key), tt)

}

发现里面为了规避内存泄露,已经通过计数的方式,定数清理掉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值