记一次内存泄漏问题的排查与修复过程

事件回顾: 我们收到容器服务告警通知,某个容器应用服务间歇性崩溃重启,该应用是使用Golang语言编写的,部署在阿里云k8s。 登录阿里云后台,查看容器应用监控指标,发现该应用是因为内存耗尽而崩溃: 从上图可以看出,大约每隔80分钟,应用耗尽内存崩溃重启。该应用部署了多个实例,每个实例内存上限为1G,平时内存占用是比较稳定,只在高峰期有波峰,其它时间几乎一条直线躺平,现在显然是不正常的。 排查步骤: 1、重现问题 把线上代码拉到本地配置模拟环境编译运行,发现在本地也能重现出内存泄漏,这样问题就很好排查了。但比较恶心的是,内存泄漏比较慢,一开始是看不出有啥问题的,要过比较长一段时间才能看出内存有在慢慢上升的,排查问题很耗时间(编译启动应用后,要观察一段时间才能判断应用是否内存泄漏)。 为此我编写了一个简单的脚本监控内存变化: 监听结果: 结果显示内存占用在不断攀升。 2、定位导致异常的代码。 在此期间已有多人提交分支代码并上线,commit比较多,不清楚到底是哪个提交代码导致问题的。因此先定位导致出现问题的代码在哪里,这里简单粗暴使用git bisect进行二分法定位。大概原理是:先回滚至前80个提交,测试发现没有内存泄漏,再回滚到前40个提交,没发现问题,再回滚至前20个提交,出现内存泄漏。OK,导致问题的代码在前40到前20的提交之间,范围缩小了。随后再次在40到20之间进行二分法,直到找到最终的有问题的代码提交,然后跟前一个代码提交进行diff对比,即可知道修改了哪些代码导致出现问题。先找出最近正常的提交, 例如7天前是正常的,查找7天前的记录,然后回滚,编译运行检查是否正常。 找到最近的正常提交后,就可以使用git bisect定位出问题的提交了: 3、使用性能剖析工具pprof分析内存 go tool pprof […]