You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 03:18:04