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

.NET Profiler未标识控制器操作循环引用的问题咨询

关于.NET循环引用对象被GC回收的疑惑解答

这问题我之前也碰到过,一开始也误以为循环引用会导致内存泄漏,理清楚逻辑后就明白为啥你看到存活实例数为0了~

首先明确:.NET GC完全能处理循环引用

.NET的垃圾回收器是追踪式回收,它会从「根对象」(比如静态变量、栈上的局部变量、CPU寄存器中的引用等)出发,遍历所有可达的对象。如果一组对象互相形成循环引用,但没有任何根对象指向这个循环链里的任何一个对象,那整个循环链都会被标记为可回收资源,GC会正常清理它们。

你的代码场景分析

看你贴的控制器代码:

[AllowAnonymous] 
public ActionResult Index() { 
    var b = new B(); 
    var c = new C(); 
    b.x = c; 
    c.x = b; 
    return PartialView(); 
}

这里的b和c都是方法内的局部变量,它们的作用域仅限于Index方法执行期间。当方法执行完毕,对应的栈帧会被弹出,这两个变量的引用就从根集合中消失了——也就是说,整个b-c循环链没有任何根引用指向它们,GC完全可以把它们回收掉。

为啥Profiler和DotMem显示存活数为0?

这是正常现象:

  • 当方法执行结束后,.NET的GC可能已经在后台完成了回收(尤其是对于短期存活的对象,会被放在第0代,回收频率很高);
  • 如果你是在方法执行完毕后才拍的Profiler快照,那这些对象已经被清理,自然看不到存活实例。

如何验证对象确实被回收了?

可以做两个小测试:

  1. 添加析构函数:给B和C类加上析构函数,运行程序看控制台输出:

    public class B
    {
        public object x;
        ~B()
        {
            Console.WriteLine("B对象被GC回收了");
        }
    }
    
    public class C
    {
        public object x;
        ~C()
        {
            Console.WriteLine("C对象被GC回收了");
        }
    }
    

    调用Index方法后,过一会儿(或者手动触发GC)就能看到控制台输出回收日志。

  2. 手动触发GC:在方法末尾手动调用GC(仅调试用,生产环境别这么干):

    [AllowAnonymous] 
    public ActionResult Index() { 
        var b = new B(); 
        var c = new C(); 
        b.x = c; 
        c.x = b;
    
        // 手动触发GC
        GC.Collect();
        GC.WaitForPendingFinalizers();
        
        return PartialView(); 
    }
    

    再拍Profiler快照,依然会看到存活实例数为0,进一步证明对象被回收了。

关于Profiler查看区域的小提示

如果之后遇到真的内存泄漏情况,你可以重点查看这些区域:

  • 静态变量是否意外持有对象引用;
  • 事件订阅是否没有取消,导致对象被订阅者持有;
  • 非托管资源是否没有正确释放(但你的场景里没有非托管资源)。
    但在当前这个场景下,你完全没找错区域,就是对象被正常回收了~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:23:03