当前位置: 代码迷 >> C# >> GC.Collect挟制垃圾回收,没有效果
  详细解决方案

GC.Collect挟制垃圾回收,没有效果

热度:615   发布时间:2016-05-05 02:46:28.0
GC.Collect强制垃圾回收,没有效果
person p1 = new person();
            p1.Name = "Tam";

            WeakReference wkr = new WeakReference(p1);
            p1 = null;  
            GC.Collect(); // 强制进行垃圾回收

            object wP1 = wkr.Target;
            if (wP1 != null)
            {
                Console.WriteLine(((person)wP1).Name);
            }
            else
            {
                Console.WriteLine("对象已被回收");
            }

            Console.ReadKey();
        }
    }
    class person
    {
        public string Name { get; set; }
    }

p1=Null的时候,代表p1之前指向的内存已经可以被回收了吧?我测试为什么还能打印出来Name呢
------解决思路----------------------
引用:
我尝试在Release模式下运行,但是结果还是和Debug一样。用多线程的话是结果是被回收了。这是GC回收机制的不确定性,还是因为线程的问题?


我试验了下,clr 2 和 clr 4 的结果并不一致。这个和 jit / gc 实现有关,不同的编译器和 clr 版本都可能导致结果差异。而且也没有官方的文档说这么细节的实现,能看到的 coreclr 的 ryujit 这一部分也可能和原来不一样。所以影响它的因素具体还有多少真很难说。

不过这并不是一个值得纠结的问题,如果你真的能在实际的代码中遇到 gc 无法回收,导致内存泄漏的问题,那时再去专门针对具体的场景进行分析处理。绝大多数情况下,gc 都能很好的工作,不需要手动 Collect。

从你这个问题里,需要了解的就是两件事:1. jit 在翻译方法的时候会生成一张表,计算出局部变量的生命周期结束的指令位置,以供 gc 参考。2. 把局部变量设置为 null 并不代表原引用立即失效,它的生命周期是 jit 计算出来的,未必是设置为 null 的位置。(背后的原因是:调用栈上方法的参数/局部变量也是一种 gc root,生命周期与 jit 生成的指令有关,实际的栈和寄存器对内存地址的引用并不一定是 c# 代码想当然的翻译)

另外,希望不要被有些说法误导。无参的 GC.Collect 对于一般对象的回收是阻塞的(有参数的重载可以进行非阻塞 gc),只不过是因为有析构器队列机制,如果需要回收的对象有析构器,那么会放到一个队列里,在 gc 的线程执行。这时,完全回收可以使用:

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
你使用多线程看到了回收的结果,不要理解错了。那是因为方法中代码改变了,会影响到 jit 计算的生命周期,而不是因为需要等待 GC.Collect 执行。你可以试验,仅仅把测试是否回收的代码移到另一个方法里就能改变结果。