ASP.NET Core中WebSocket请求使用时被垃圾回收的原因
ASP.NET Core WebSocket场景下资源被GC回收但连接仍保持的问题
代码示例
[Route("ws")] public async Task WebSocket() { using (var data = new Data()) { if (HttpContext.WebSockets.IsWebSocketRequest) { using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); var tcs = new TaskCompletionSource(); // 此处为WebSocket业务代码,已移除以简化复现 await tcs.Task; } else { HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest; } } } class Data : IDisposable { public void Dispose() { throw new NotImplementedException(); } ~Data() { Console.WriteLine("Data disposed"); } }
问题描述
data及注释位置的其他资源已被垃圾回收,但WebSocket仍处于连接状态,TaskCompletionSource未完成、Action任务也未完成。该代码与微软ASP.NET Core文档中的WebSocket示例非常接近,为何会出现这种情况?
原因分析
- using块的执行时机:
using (var data = new Data())的Dispose()方法会在整个代码块执行完毕后才调用,但代码执行到await tcs.Task时会挂起,整个using块并未结束,所以Dispose()并未触发。你看到的"Data disposed"是对象终结器(~Data())的输出,说明data对象被GC回收了。 - GC的回收逻辑:
data变量在进入WebSocket分支后,没有再被任何代码引用,即使它还处于using块的作用域内,GC依然可以回收这个不再被使用的对象。GC只关心对象是否有活跃引用,不关心它是否在using块中。 - 请求Task未被回收:ASP.NET Core并没有回收请求Task,因为
await tcs.Task让整个Action任务处于挂起等待状态,所以WebSocket连接能保持。微软官方示例中通常会在WebSocket的消息处理循环中持续持有资源引用,或者资源在会话全程被使用,所以不会被GC回收,而你的代码中data提前失去了引用,才会被GC回收。
解决建议
- 如果
Data对象需要在WebSocket会话全程存活,要在WebSocket的业务处理逻辑中保持对它的引用,比如把它传入处理方法、或者作为局部变量保留引用,避免GC回收。 - 正确实现
IDisposable接口,确保Dispose()能正常执行(当前代码中Dispose()抛出异常,会导致using块结束时出错),如果需要在会话结束时释放资源,应该在tcs.SetResult()之后让using块正常结束,触发Dispose()。
内容的提问来源于stack exchange,提问作者maloo
相关产品推荐
相关产品推荐

