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

如何定位未释放的Closure闭包对象的创建位置?

关于闭包类与内存泄漏的问题解答

闭包后缀c__DisplayClass7_0的本质

  • 这个后缀对应的是编译器在编译阶段自动生成的显示类(Display Class),是C#实现闭包的底层机制——当lambda或匿名方法引用外部变量时,编译器会自动创建这类类来持有被捕获的变量。
  • 这类类仅存在于编译后的IL代码中,源代码里没有定义,因此全局搜索源代码找不到它,必须通过反编译工具(如你使用的ILSpy)查看生成的IL或反编译后的代码才能定位。

你的闭包来源分析

从你补充的代码来看,闭包来自修改后的清理方法里的这行代码:

References.RemoveAll(x => x == remove);

因为这个lambda表达式引用了方法内的局部变量remove,编译器自动生成Stuff<OtherStuff>+<>c__DisplayClass7_0类,将remove作为该类的成员变量持有,以此实现闭包捕获——这个生成的闭包对象就是你在内存泄漏排查中发现的未释放对象。

关于static lambda编译失败的原因

你尝试写成References.RemoveAll(static x => x == remove)时编译失败,核心原因是**static lambda不允许捕获外部的局部变量或实例成员**。static lambda仅能访问静态变量或常量,而remove是方法内的局部变量,不在其允许访问的范围内,因此无法通过编译。

优化建议(避免闭包+保持性能)

如果想要避免生成闭包对象,同时保留性能优势,可以不用lambda,改用手动实现移除逻辑:

lock (ThreadLock)
{
    WeakReference<T>? remove = null;
    for (int i = References.Count - 1; i >= 0; i--)
    {
        if (!References[i].TryGetTarget(out T? t) || t is null)
        {
            if (remove is null)
                remove = References[i];
            else
                References[i] = remove;
        }
    }
    if (remove is not null)
    {
        // 手动遍历移除所有等于remove的元素
        for (int i = References.Count - 1; i >= 0; i--)
        {
            if (References[i] == remove)
            {
                References.RemoveAt(i);
            }
        }
    }
}

这种方式不需要依赖lambda,也就不会生成闭包对象,同时避免了RemoveAll可能带来的额外开销。

内容的提问来源于stack exchange,提问作者Betty Crokker

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 06:50:26