排查ASP.NET Core 2.0 WebSocket服务器内存泄漏问题
排查ASP.NET Core 2.0 WebSocket服务器内存泄漏的实战思路
Hey,针对你遇到的ASP.NET Core 2.0 WebSocket服务器内存泄漏问题,我分享几个实战中常用的排查思路和步骤,帮你定位问题:
一、先确认「真泄漏」还是「假报警」
Windows任务管理器的内存数值容易误导人——它显示的是整个进程的工作集内存,包含CLR尚未回收的暂用内存、非托管内存等。建议先换用专业工具验证:
- 用Visual Studio自带的「内存诊断工具」或dotMemory,抓取进程的托管堆内存快照,对比多次压力测试后的对象数量变化,只有当某类对象持续增长且无法被GC回收时,才是真正的泄漏。
- 测试时注意:每次压力测试后,给服务器留足GC回收时间(比如30秒以上),再抓取快照,避免把临时内存占用当成泄漏。
二、WebSocket场景的高频泄漏点排查
1. 连接资源未彻底清理
WebSocket的异步操作(ReceiveAsync/SendAsync)和连接状态管理是重灾区:
- 务必确保在连接关闭(无论正常还是异常断开)时,调用
WebSocket.CloseAsync并等待完成,最后调用Dispose释放资源。 - 异常分支容易被忽略:比如客户端意外断开时,要检查
WebSocket.State,如果是Open状态,必须主动关闭并释放。 - 示例代码参考:
public async Task HandleWebSocketAsync(HttpContext context) { var webSocket = await context.WebSockets.AcceptWebSocketAsync(); try { var buffer = new byte[4096]; WebSocketReceiveResult result; do { result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); // 业务消息处理逻辑 } while (!result.CloseStatus.HasValue); await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); } catch (Exception ex) { // 异常日志记录 if (webSocket.State == WebSocketState.Open) { await webSocket.CloseAsync(WebSocketCloseStatus.InternalServerError, "Server error", CancellationToken.None); } } finally { // 从连接存储集合中移除当前连接(如果有) _activeConnections.TryRemove(context.ConnectionId, out _); webSocket.Dispose(); } }
2. 静态集合持有无效连接实例
如果用静态集合(比如List<WebSocket>)存储活跃连接,很容易出现泄漏:
- 必须用线程安全集合(比如
ConcurrentDictionary<string, WebSocket>),避免并发操作导致的对象残留。 - 连接关闭时,一定要从集合中移除对应实例,否则GC无法回收这些被静态引用持有的对象。
3. 非托管资源泄漏
如果你的WebSocket服务器用到了原生库或非托管代码,要检查是否正确实现了IDisposable模式,确保非托管句柄在对象生命周期结束时被释放。
三、测试环节的优化
你用的Thor是不错的压力测试工具,但可以调整测试策略来辅助排查:
- 分批次测试:比如先执行
thor --amount 100 ws://your-server-url,等待内存稳定后记录;再执行thor --amount 200 ...,对比两次内存变化,如果内存增长和连接数不成正比,大概率有泄漏。 - 测试后重启验证:如果重启服务器后内存回到初始值,说明是进程内的累积泄漏;如果重启后内存依然偏高,可能是系统级的资源残留(比如端口、句柄泄漏)。
四、ASP.NET Core 2.0版本特有的注意点
ASP.NET Core 2.0的WebSocket中间件存在一些已知的内存相关bug,建议:
- 升级到2.0的最新补丁版本,修复官方已确认的问题。
- 如果可能,考虑升级到ASP.NET Core 2.1及以上版本(2.1引入了WebSocket的性能和内存优化,比如连接池改进)。
五、终极定位方法:内存快照对比
用dotMemory或Visual Studio内存诊断工具,抓取三次快照:
- 服务器启动后的初始快照。
- 第一次压力测试后的快照。
- 第二次压力测试后的快照。
对比快照中持续增长的对象类型,找到对应的代码位置——比如如果WebSocket实例持续增长,说明连接未被正确释放;如果是自定义的业务对象增长,说明业务逻辑中存在对象持有问题。
内容的提问来源于stack exchange,提问作者Bercovici Adrian
相关产品推荐
相关产品推荐

