记灰度环境一次碰到服务OOM的问题的排查过程与解决方法
记一次服务端OOM问题排查过程与解决方法
背景
起因是改了服务算法里匹配部分的代码,从串行改为并行,然后丢到灰度环境,重放线上流量时,容器内存占用上涨不少,几G有时候会上涨到二十G,然后触发OOMKILL。
排查过程
profile直接看实际内存占用
首先排除是Golang runtime归还内存给操作系统的策略导致,因为之前踩过MADV_FREE的坑。现在用的版本是Go1.16,归还策略已经改回去了
- 查看内存占用,采集上涨前后对比,发现inuse的堆内存并没有实际那么高,最多才1G,而且前后没有明显的上涨,跟监控里服务实际使用内存相差太多了
- 进一步看meminfo分析,服务在不断进行大量分配和回收内存
- 切换一下,查看alloc的内存,发现大量分配内存在代码里两个字符串最长公共子序列LCS的计算上
- 观察算法,就是很正常M*N的二维数组动规算法来算LCS,咋一看好像没啥问题,总共才十来行代码
- 拿线上数据本地跑一下发现,有几千词的字符串占用大小是30KB左右,跟相同差不多长度的匹配时,一个gorouine就上百MB,我改成并行后就上1GB往上了,更别说实际服务有几十个worker运行,所以的确能触发这么高的内存占用
处理方法
两个方向:
- 其实来到算法这里的字符串正常不应该这么长,在前面步骤应该裁剪到一定程度,不过这是业务部分的逻辑。
- 优化DP算法,正常DP的状态转移记录都是可以压缩的,把二维数组压缩到一维,将O(N*M)的空间复杂度减少至O(N)复杂度,即可解决。
后记
改完后内存占用直接降到1G以内,数据结构与算法果然是第一生产力。