.NET Core使用System.Net.WebSockets出现委托被GC提前回收报错如何解决
问题根因
该错误核心是WebSocket异步操作绑定的委托对应的托管对象没有被托管侧持有强引用,运行一段时间后被GC提前回收。由于非托管的网络IO层持有的对象引用无法被GC识别,GC会判定该对象无引用并释放,等回调触发时目标对象已不存在,就抛出该错误同时中断WebSocket连接。
修复方案
- 避免委托绑定局部对象:不要让WebSocket的ReceiveAsync/SendAsync相关回调绑定方法内的局部变量/匿名对象,方法执行完毕后局部对象无其他强引用就会被GC回收。需要把WebSocket实例、接收缓冲区、连接处理器等关联对象提升为类的实例字段/静态字段,用类的实例或静态引用长期持有强引用,避免被回收。
错误代码示例:
// 错误写法:ws是局部变量,方法执行结束后无强引用会被GC回收 async Task InitWebSocket() { var ws = new ClientWebSocket(); await ws.ConnectAsync(serverUri, CancellationToken.None); var buffer = new byte[4096]; // 后台异步接收,方法直接返回后局部对象无引用 _ = ws.ReceiveAsync(buffer, CancellationToken.None).ContinueWith(t => HandleRecvResult(t, ws)); }
正确代码示例:
// 正确写法:用类字段持有WebSocket和关联对象的强引用 private ClientWebSocket _clientWs; private byte[] _recvBuffer; async Task InitWebSocket() { _clientWs = new ClientWebSocket(); await _clientWs.ConnectAsync(serverUri, CancellationToken.None); _recvBuffer = new byte[4096]; // 启动长生命周期的接收循环 _ = RunReceiveLoop(); } async Task RunReceiveLoop() { while (_clientWs.State == WebSocketState.Open) { var result = await _clientWs.ReceiveAsync(_recvBuffer, CancellationToken.None); // 处理接收到的数据 HandleRecvResult(result); } }
- 控制关联对象的生命周期:不要用using块包裹生命周期长于方法执行周期的WebSocket实例,using块执行完毕会主动Dispose对象,后续回调触发时对象已被释放。如果必须使用using,要确保using块的生命周期覆盖整个WebSocket连接的存活周期。
- 特殊场景手动固定对象引用:如果业务场景必须使用独立回调,可以调用
GC.KeepAlive(你的对象)在连接存活期间持有对象引用,或者将对象添加到全局静态强引用列表中,等连接主动关闭后再从列表移除,避免GC回收。 - 检查取消令牌回调:如果给WebSocket操作注册了CancellationToken的回调,同样要确保回调绑定的对象有强引用,不要绑定局部对象。
验证方法
修复后可以在测试环境手动触发频繁GC(调用GC.Collect()、GC.WaitForPendingFinalizers()),如果连接不会主动中断、不再抛出该错误,说明修复生效。
内容的提问来源于stack exchange,提问作者otter otter
相关产品推荐
相关产品推荐

