● 公司核心系统"内存泄露"排查(完结)

一、前言

这个问题本来不是特别亟待解决的问题,但是堵在心里实在难受,总想知其然,并知其所以然。

先说结论:不是内存泄露,是asp.net垃圾回收机制问题:默认情况下,服务器CPU内核数决定asp.net core内存垃圾回收策略。纯属基础不扎实,凌晨写文章记录并反省。


一个月前开始分析csredis的源码,但是没有头绪。便在MacOS/windows/ubuntu测试,多次测试pub/sub均未复现,内存一直稳定在几十MB左右,并无增涨。

二、多角度测试

review了很多遍代码,都没找到问题,所以我开始了几个测试:

  1. 测试HttpClient;
  2. csredis更换为freeredis;
  3. 拆分Controllers,每个实例只运行一个Controller;

1. 测试HttpClient

为了能快速查看内存增涨情况,我写了个死循环异步请求HttpClient的接口,同时也使用ab进行压测。分别放到windows/ubuntu测试,发现内存确实上涨了,但是很快便稳定在一个数值,大约200MB左右。

2. csredis更换为freeredis

csredis更换为freeredis后放到生产服务器,发现还是存在内存上涨情况,dumpheap也没发现任何问题。

3.拆分Controllers,每个实例只运行一个Controller

拆分Controllers为多个实例后,放到生产服务器模拟请求,发现所有Controller都存在内存上涨情况。

这个结论就跟诡异,只有生产服务器存在内存上涨情况,我开始怀疑生产服务器的系统问题(CentOS 7.4)。

三、Google大法

多亏了Google,百度真的搜不到有用的资料。

这个人在问asp.net core在docker下的GC机制,跟我遇到的问题类似,离解决很接近了:

这个人在问asp.net core HttpClient内存泄漏问题,但是我已经排除了HttpClient问题:

四、偶然发现

这里有2个偶然发现:

  1. 在生产服务器测试web api的时候,另一个web api内存出现了下降的情况。这样就排除了内存泄漏,明显内存是被回收了。
  2. 通过Google搜到了一条 Github KestrelHttpServer 2016年.net core 1.1的issuemy server keeps using memory at about ~0.5 Mb per minute 的描述与我的情况一致,如下图:

除了上面的偶然发现,我又看到下面有人在2017年描述的问题,发现一个很重要的提示。

  1. 他在给Google云上的K8S设置内存为500MB,但是asp.net core超过了这个限制并且自动重启;限制到1000MB时,进程的占用到了650MB左右就不再增加,如下图所示:

我想起来有两台服务器都在跑这个asp.net core,登录了其中一台维护时间比较长的服务器,查看了pid后,看下进程运行的时长=1760331/3600/24,大约不到21天:

ps -p $PID -o etimes
ELAPSED
1760331

查了下监控程序记录在数据库的数据,内存占用前10天左右一直在缓慢增涨,后面一直很稳定。完全符合这群老外讨论的内容,看来真相已经很接近了。

  1. 下面有个大神Tim Seaward回复到:CPU核心数,对内存垃圾回收有决定性的影响。如下图:

我在各个服务器运行Environment.ProcessorCount后,结果如下:

#测试服务器
Windows Server 2008R2 DataCenter: 4
Ubuntu 16.04: 12
MacOS: 8

#生产服务器
CentOS: 2
  1. Tim Seaward给出了微软官方的解释和解决方案:

  1. 查了Microsoft Docs相关描述如下:asp.net core默认为Server GC。两台生产服务器都是2核,所以Server GC在64位机器上的临时段默认大小为4GB。看来2核CPU、8GB内存的服务器如果业务比较多,asp.net内存真的不够用。

  1. Microsoft Docs在 Fundamentals of garbage collection 给出了解决方案,和上面Tim Seaward给出的方案一致,更改设置GC模式为Workstation GC。microsoft docs的标题也很羞辱——Fundamentals不知道是翻译为基本原理还是基础知识。还是怪自己基础不扎实,所有问题应该第一时间在Microsoft Docs找答案。

最后,Server GC更换为Workstation GC,微软也给出了提醒:

如果你觉得CPU使用率比内存更重要,内存比较充裕不敢随意使用CPU,Server GC更好;如果内存使用率较高、CPU使用率相对较低,想把压力分给CPU一点去更频繁GC,则Workstation GC的性能可能更高。

目前一台生产服务器2核CPU+8GB内存,内存占用<2GB,适合Server GC模式;另一台运行了GitLab等一堆业务,2核CPU+8GB内存,内存占用>5GB,显然Workstation GC模式更适合。

-------- END --------

参考资料: