背景

某应用跑了一段时间后,占用内存过大,引发内存告警。虽然应用工作正常,但是告警存在心里不舒服,因此决定分析一下原因。

该应用是Tcp服务端,解析JT/T808协议并处理各指令业务逻辑。包括向外转发数据、访问MySql/Redis/RocketMQ、访问第三方Http接口等。根据设计,每进程最大承担2000台设备长连接。下图是某进程负载600台设备长连接时的内存占用情况。

分析

该应用进程占用内存961.4M,其中包括631.2M的非托管内存,显然非托管内存过大。同时发现,0代GC内存快速增长的同时,非托管内存同步减少,整个进程总内存不变。因此可以大胆猜测,那个非托管内存,就是net7提前向操作系统申请的内存空间,为了后续能够快速分配内存,提升吞吐。

查询各种资料后,尝试修改“RetainVMGarbageCollection”参数,参考:《垃圾回收器配置设置》

<RetainVMGarbageCollection>false</RetainVMGarbageCollection>

更新应用后,观察两天,发现内存占用还是那么多,没有变化。有些资料说,这个参数默认值就是false。

改进

在文章《垃圾回收器配置设置》中,发现了很多有意思的参数,比如“GCConserveMemory”。只说了runtimeconfig.json配置,没有MSBuild项目配置,先搁置。

再查各种资料,发现绝大部分资料都在讲“System.GC.Server”。但我的应用引入了WebApi,GCServer默认就是true,改为false的话可能导致性能下降(主要是吞吐下降)。

GCServer=true 时,即服务器模式,每个处理器分批一个堆,最大化利用内存;

GCServer=false 时,即工作站模式,只有一个堆,GC回收将会变得更加积极,内存占用更小;

最后决定尝试工作站模式,直接修改项目:

<ServerGarbageCollection>false</ServerGarbageCollection>

借助星尘发布应用更新到服务器上,观察几个小时:

内存占用大幅下降,非托管内存占用很小。2代变大了,可能是因为GC更频繁所致。