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

半个菜鸟 2015-09-25 02:04:16
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呢
...全文
2956 27 打赏 收藏 转发到动态 举报
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
fanfan_gg 2018-07-09
  • 打赏
  • 举报
回复
引用 7 楼 crystal_lz 的回复:
[quote=引用 3 楼 qq_26770005 的回复:]
[quote=引用 1 楼 crystal_lz 的回复:]
首先 GC.Collect(); 并不会立即去回收 只是告诉回收期 去回收
就像 叫你 起床吃饭了 只是 告诉了你 你应该起床吃饭了 不代码 你就会 马上起来

还有 你的 p1 被 wkr 引用着 就算要 回收期立即回收 也不会释放他
你的 p1 设置成了 null 并不代码 你 wkr 里面的引用 就是null 了
对于引用类型 的变量 其保存的只是一个内存地址 假设 p1 这个对象是在 内存中 0x000000FF 这个位置上
那么 你的 wkr.Target = p1 的时候 那么 wkr.Target 也会指向 0x000000FF 这个地方
而你 设置 p1 = null 的时候 只是上 p1 的引用变为了 0x00000000 而已 但 wkr.Target 还是 0x000000FF
所以 你通过 wkr.Target 已让能访问到数据

1,被WeakReference引用的也是可以被回收
2,当我把P1指向null以后,原来P1指向的内存已经是没有被别的变量指向了,就是说以被回收了
3,如果我没有执行GC.Collect(),有可能是可以找到P1.Name的值的
4,GC.Collect()是即时回收垃圾,也就是说是立刻执行垃圾回收的。执行这句代码后p1原先指向的那个内存应该是被立刻回收了的
[/quote]

是谁告诉你的 被引用也是可以被回收?
我吧 t1 设置 为 null 也没见 t2 被回收啊[/quote]

先搞清楚 WeakReference 是什么再说
  • 打赏
  • 举报
回复
明智的我不参加这种讨论,果然是正确的因为我也没搞懂gc.collection到底是怎么执行的,尤其还可以指定回收几代
  • 打赏
  • 举报
回复
析构函数全部继承于object的finalize方法,你说的这个析构队列只包含重载了finalize的对象吗? 我脑残,还抱着老黄历找Reflector呢,结果发现被一个英国公司买了,于是揍去买了个标准版,才$99 emoji酷啊,这team里要是有几个女生和伪娘,一堆emoji,code review很艰难的说! coreclr也是微软的,它和咱们用的标准clr有啥区别? 关于GC.Collect不同行为的问题,我觉得如果可以获取编译时的完整flags,就应该可以看出区别吧?不过照你这么说,在MSIL这步,大家很可能都是一样的,只是到了JIT的时候才出现的不同结果? LINQ你揍不要再提了,越描越黑啦! MS不是保证说不会影响编译后结果吗? 移植到coreclr?这是毛意思?夸平台到linux吗?mono不好使吗?MS不是自己也要搞吗? 自己写GC?给你的应用写一个不难,写一个generic的,揍呵呵了! 成了,给你写了这么多,别说我们不鼓励你技术讨论。 你还蛮特别的,.NET版像你这样肯吊书袋的不多! .net版一堆的MVP,不知道MCSD有几个!
引用 23 楼 github_22161131 的回复:
[quote=引用 20 楼 small21 的回复:] 不要用什么打印引用判断是否回收了,你在person中写个析构函数就行了,运行到了就说明回收了。
GC 对待有析构器的对象是不一样的(之前提到的析构队列机制,有析构会导致更慢的回收,一般除非直接引用了非托管资源,否则不应该使用析构),所以这里不能放在一起比较。
引用 21 楼 conmajia 的回复:
这种没有意义的问题还讨论的有板有眼的。。。混到需要手动gc你是有多挫。。。
无论如何,讨论技术也比空洞和讽刺的评论有意义。你也了解底层知识,应该也明白探索精神有多么重要。大部分情况下不需要手动gc,但是它绝对值得讨论。你是说这个 API 暴露出来就是给挫人用的么?我也很想了解下需要“有板有眼”讨论的是什么问题,可否举例对比下意义。
引用 19 楼 micropentium6 的回复:
居然把ms官方反编译拿出来说事儿了,你也够拼的! 费这么大劲儿,给我看这个不就结了!
你可以试试 Ref12 插件,一点不费劲。那个是源码,有注释和被编译器去掉的代码,比反编译的好。(顺便安利下 EmojiVS 这个插件~)
引用 19 楼 micropentium6 的回复:
_Collect的实现好想木有给啊!非托管代码吧?
当然是非托管的,它最后调用到了 gc.cpp 里面的 GCHeap::GarbageCollect (int generation, BOOL low_memory_p, int mode)
引用 19 楼 micropentium6 的回复:
对了,我还去看了一眼mcs的结果,很可悲,咱们这写的这些垃圾一律木用。
coreclr 的结果倒是和 clr 一致。
引用 19 楼 micropentium6 的回复:
我最后的话更没价值?那你来解释下,为毛会有这种情况?给咱来点有价值的! 既然MS划好了圈圈,安心在里面玩揍是了。托管的世界多美好啊!
难道不是么?要不然解释下这么说价值何在。 找了台 vs2012 的机器试验,确实debug下也可以回收。进一步发现,只要不使用 RyuJIT 就是和之前版本一致的结果(#16 说的 clr 2 和 clr 4 不一致的结果也是 RyuJIT 导致的)。那么现在看起来,是 LZ 的情况最为奇怪,Release 都不能回收就不太正常了。 这里还有个坑,如果 var p1 = new person { Name = "Tam" }; 这么写的话,会产生一个看不见的临时变量引用这个对象,后面 p1 = null; 不会作用于这个临时变量,也能导致 Debug 下无法回收。 我这么热衷这个问题一方面是因为有东西要移植到 coreclr,需要深入了解新的 .net 开源体系;另一方面是因为原来看过 LuaJIT 的 新 GC 设计,现在又被勾起了写个GC玩玩的冲动。其实需要说的有 #16 就够了,后面的权当废话吧。[/quote]
半个菜鸟 2015-09-29
  • 打赏
  • 举报
回复
引用 23 楼 github_22161131 的回复:
找了台 vs2012 的机器试验,确实debug下也可以回收。进一步发现,只要不使用 RyuJIT 就是和之前版本一致的结果(#16 说的 clr 2 和 clr 4 不一致的结果也是 RyuJIT 导致的)。那么现在看起来,是 LZ 的情况最为奇怪,Release 都不能回收就不太正常了。 这里还有个坑,如果 var p1 = new person { Name = "Tam" }; 这么写的话,会产生一个看不见的临时变量引用这个对象,后面 p1 = null; 不会作用于这个临时变量,也能导致 Debug 下无法回收。 我这么热衷这个问题一方面是因为有东西要移植到 coreclr,需要深入了解新的 .net 开源体系;另一方面是因为原来看过 LuaJIT 的 新 GC 设计,现在又被勾起了写个GC玩玩的冲动。其实需要说的有 #16 就够了,后面的权当废话吧。
真是受宠若惊,这位大神的解释让我很是茅塞顿开。其实我只是一个刚入门的菜鸟,因为老师讲到GC回收这个知识点,然而我和老师的代码完全一样执行结果却不一样,所以那天晚上才发了个帖子问。 请看图: 顺便说一句,我用的是VS2013版。 这个问题我一直弄不明白真正原因,姑且只能相信是GC回收的代码即使写出来也不是立刻能够全部将垃圾回收掉这个说法了
Conmajia 2015-09-29
  • 打赏
  • 举报
回复
这种没有意义的问题还讨论的有板有眼的。。。混到需要手动gc你是有多挫。。。
winnowc 2015-09-29
  • 打赏
  • 举报
回复
引用 20 楼 small21 的回复:
不要用什么打印引用判断是否回收了,你在person中写个析构函数就行了,运行到了就说明回收了。
GC 对待有析构器的对象是不一样的(之前提到的析构队列机制,有析构会导致更慢的回收,一般除非直接引用了非托管资源,否则不应该使用析构),所以这里不能放在一起比较。
引用 21 楼 conmajia 的回复:
这种没有意义的问题还讨论的有板有眼的。。。混到需要手动gc你是有多挫。。。
无论如何,讨论技术也比空洞和讽刺的评论有意义。你也了解底层知识,应该也明白探索精神有多么重要。大部分情况下不需要手动gc,但是它绝对值得讨论。你是说这个 API 暴露出来就是给挫人用的么?我也很想了解下需要“有板有眼”讨论的是什么问题,可否举例对比下意义。
引用 19 楼 micropentium6 的回复:
居然把ms官方反编译拿出来说事儿了,你也够拼的! 费这么大劲儿,给我看这个不就结了!
你可以试试 Ref12 插件,一点不费劲。那个是源码,有注释和被编译器去掉的代码,比反编译的好。(顺便安利下 EmojiVS 这个插件~)
引用 19 楼 micropentium6 的回复:
_Collect的实现好想木有给啊!非托管代码吧?
当然是非托管的,它最后调用到了 gc.cpp 里面的 GCHeap::GarbageCollect (int generation, BOOL low_memory_p, int mode)
引用 19 楼 micropentium6 的回复:
对了,我还去看了一眼mcs的结果,很可悲,咱们这写的这些垃圾一律木用。
coreclr 的结果倒是和 clr 一致。
引用 19 楼 micropentium6 的回复:
我最后的话更没价值?那你来解释下,为毛会有这种情况?给咱来点有价值的! 既然MS划好了圈圈,安心在里面玩揍是了。托管的世界多美好啊!
难道不是么?要不然解释下这么说价值何在。 找了台 vs2012 的机器试验,确实debug下也可以回收。进一步发现,只要不使用 RyuJIT 就是和之前版本一致的结果(#16 说的 clr 2 和 clr 4 不一致的结果也是 RyuJIT 导致的)。那么现在看起来,是 LZ 的情况最为奇怪,Release 都不能回收就不太正常了。 这里还有个坑,如果 var p1 = new person { Name = "Tam" }; 这么写的话,会产生一个看不见的临时变量引用这个对象,后面 p1 = null; 不会作用于这个临时变量,也能导致 Debug 下无法回收。 我这么热衷这个问题一方面是因为有东西要移植到 coreclr,需要深入了解新的 .net 开源体系;另一方面是因为原来看过 LuaJIT 的 新 GC 设计,现在又被勾起了写个GC玩玩的冲动。其实需要说的有 #16 就够了,后面的权当废话吧。
  • 打赏
  • 举报
回复
首先,看过代码后,我承认你是对的!我写的和gc.collect是一样的,forcing是default行为,被定义成0了。。。 居然把ms官方反编译拿出来说事儿了,你也够拼的! 费这么大劲儿,给我看这个不就结了! https://msdn.microsoft.com/en-us/library/bb384155(v=vs.110).aspx 你说的好玩的事真的发生了:在我笔记本上的vs2013里,debug环境下(target: 4.5, runtime:4.0),怎么写都可以回收。为毛呢?你自己去研究吧!哦对了,_Collect的实现好想木有给啊!非托管代码吧?要不,你看看ML怎么说? 对了,我还去看了一眼mcs的结果,很可悲,咱们这写的这些垃圾一律木用。 我最后的话更没价值?那你来解释下,为毛会有这种情况?给咱来点有价值的! 既然MS划好了圈圈,安心在里面玩揍是了。托管的世界多美好啊!
引用 18 楼 github_22161131 的回复:
引用 17 楼 micropentium6 的回复:
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true); 和 GC.Collect(); 是等价的,那段代码和 LZ 原来的没什么区别,效果是相同的,我没看出来要表达什么(莫非你那里 Debug 模式下这样可以成功回收?那就更好玩了)。这里的讨论可能对 LZ 来说是没多少价值,我前面也提到过,不过你最后的话是属于更没价值的那种。
winnowc 2015-09-28
  • 打赏
  • 举报
回复
引用 17 楼 micropentium6 的回复:
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true); 和 GC.Collect(); 是等价的,那段代码和 LZ 原来的没什么区别,效果是相同的,我没看出来要表达什么(莫非你那里 Debug 模式下这样可以成功回收?那就更好玩了)。这里的讨论可能对 LZ 来说是没多少价值,我前面也提到过,不过你最后的话是属于更没价值的那种。
慧眼识狗熊 2015-09-28
  • 打赏
  • 举报
回复
不要用什么打印引用判断是否回收了,你在person中写个析构函数就行了,运行到了就说明回收了。
  • 打赏
  • 举报
回复
首先文章没有写错,因为MSDN文档就是这么写的,sp1234,你要接着跟我纠结MSDN文档的正确性。我可以forward给你一个email, 你去和MS的人吵吧! https://msdn.microsoft.com/en-us/library/xe0c2357(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1 Use this method to try to reclaim all memory that is inaccessible. It performs a blocking garbage collection of all generations. 请注意加粗字体。特别是try to这个字眼。 其次,纠结GC何时回收内存是件可笑的事。你使用GC的目的不就是为了避免纠缠内存分配和回收的痛苦吗?以下代码可以证明GC.Collect functions as it is supposed to be。但这真是你想要的吗?

class GarbageCollect
    {
        public static void ForceCollect()
        {
            object obj = new object();
            WeakReference wr=new WeakReference(obj);
            obj=null;
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
            object wP1 = wr.Target;
            if (wP1 != null)
            {
                Console.WriteLine("obj is still alive");
            }
            else
            {
                Console.WriteLine("gc is done");
            }

        }
    }
了解了GC的概念,知道了generation,big object heap等,貌似对这个问题也木有帮助是吧?在CLR这个level还要反编译ML出来说明问题,你们也太C了!既然MS划好了圈圈,安心在里面玩揍是了。托管的世界多美好啊!
引用 16 楼 github_22161131 的回复:
引用 11 楼 sp1234 的回复:
引用 13 楼 qq_26770005 的回复:
半个菜鸟 2015-09-27
  • 打赏
  • 举报
回复
引用 12 楼 github_22161131 的回复:
[quote=引用 11 楼 sp1234 的回复:] 如果你看到一篇文章上说“DEBUG下就不会在当前方法完毕前释放对象,而RELEASE下就会在当前方法执行完毕钱释放对象”,那么你看我上面对代码的修改就能知道了,这篇文章写错了!
你被先入为主的知识误导了(其他人也是),可以在你的代码中 Console.ReadKey(); 之后加上一行貌似没用的 if (false) { } 看看。 我引用了一篇文章,不代表我就看过一篇文章,它写的完全正确。这个问题是 JIT 的优化引起的,Debug 模式下,在你的例子里,只要 GC.Collect(); 之后存在 br 这种分支 IL,就不会回收。也可以再看看https://github.com/dotnet/coreclr/blob/master/Documentation/botr/ryujit-overview.md#gc-info。[/quote] 我尝试在Release模式下运行,但是结果还是和Debug一样。用多线程的话是结果是被回收了。这是GC回收机制的不确定性,还是因为线程的问题?
winnowc 2015-09-27
  • 打赏
  • 举报
回复
引用 11 楼 sp1234 的回复:
如果你看到一篇文章上说“DEBUG下就不会在当前方法完毕前释放对象,而RELEASE下就会在当前方法执行完毕钱释放对象”,那么你看我上面对代码的修改就能知道了,这篇文章写错了!
你被先入为主的知识误导了(其他人也是),可以在你的代码中 Console.ReadKey(); 之后加上一行貌似没用的 if (false) { } 看看。 我引用了一篇文章,不代表我就看过一篇文章,它写的完全正确。这个问题是 JIT 的优化引起的,Debug 模式下,在你的例子里,只要 GC.Collect(); 之后存在 br 这种分支 IL,就不会回收。也可以再看看https://github.com/dotnet/coreclr/blob/master/Documentation/botr/ryujit-overview.md#gc-info
winnowc 2015-09-27
  • 打赏
  • 举报
回复
引用 13 楼 qq_26770005 的回复:
我尝试在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 执行。你可以试验,仅仅把测试是否回收的代码移到另一个方法里就能改变结果。
  • 打赏
  • 举报
回复
例如你在 ReadChar() 语句之前访问了一下 wkr.Target 变量 --> 例如你在 ReadKey() 语句之后访问了一下 wkr.Target 变量
  • 打赏
  • 举报
回复
引用 13 楼 qq_26770005 的回复:
我尝试在Release模式下运行,但是结果还是和Debug一样。用多线程的话是结果是被回收了。这是GC回收机制的不确定性,还是因为线程的问题?
在 Release 下应该很快就被回收了,我不知道你是怎样测试的,可能你的机器内存比较大(不小于8G)并且windows下开的进程和服务太少吧,造成了 GC 智能地延迟了。 Debug 下的 GC回收对象的行为确实就会推迟,调试器要额外跟踪所有变量和堆栈,甚至可能是WeakReference 类本身在 Release 和 Debug 下分别有两个版本以方便调试,因此它干扰了 GC。例如你在 ReadChar() 语句之前访问了一下 wkr.Target 变量,那么子线程中打印的可能就是“对象没有释放”。而如果仅仅打印 wkt.IsAlive 则不会影响 GC。但是总之,如果在子线程中打印到变量已经释放,而主线程中的方法过程并没有结束,这就说明了 GC 确实是可以自动在后台销毁对象的,而不受宿主过程是否执行到 End 结束的影响。
  • 打赏
  • 举报
回复
如果你看到一篇文章上说“DEBUG下就不会在当前方法完毕前释放对象,而RELEASE下就会在当前方法执行完毕钱释放对象”,那么你看我上面对代码的修改就能知道了,这篇文章写错了!
圣殿骑士18 2015-09-26
  • 打赏
  • 举报
回复
另外,执行垃圾回收,是需要时间的,或许,你可以尝试在设置null之后,sleep(2000),等待一下,再检查引用是否释放
圣殿骑士18 2015-09-26
  • 打赏
  • 举报
回复
请看c#高级编程里讲的,注意第二标记处,垃圾收集器不能某次的垃圾收集中保证所有资源一定被释放。也就是说,它有自己的算法。你想这么做并不是可靠的方法。
winnowc 2015-09-26
  • 打赏
  • 举报
回复
根本不是上面说的那些原因,你改成 Release 模式编译运行就行了。其实也不需要设置成 null,如果想了解细节,看这里:http://stackoverflow.com/questions/17130382/understanding-garbage-collection-in-net#17131389
加载更多回复(7)

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧