如何定位未释放的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
相关产品推荐
相关产品推荐

