San*_*eep 45 .net c# garbage-collection finalizer
好吧,众所周知,当GC将Finalize对象标识为垃圾时,它会隐式调用对象上的方法.但如果我这样做会发生什么GC.Collect()?终结者还在执行吗?也许是一个愚蠢的问题,但有人问我这个,我回答"是",然后我想:" 这完全正确吗? "
Eri*_*ert 75
好吧,众所周知,当GC将对象标识为垃圾时,它会隐式调用对象上的Finalize方法.
不不不.这是未知的,因为为了成为知识,陈述必须是真实的.那句话是错误的.垃圾收集器在跟踪时不会运行终结器,无论是自身运行还是调用Collect. 在跟踪收集器找到垃圾后,终结器线程运行终结器,并且在调用时异步发生Collect.(如果它发生了,它可能不会,正如另一个答案所指出的那样.)也就是说,在控制返回之前,你不能依赖于终结器线程执行Collect.
这是一个过于简单的草图,说明它是如何工作的:
正如我所说的那样,过于简单; 终结器队列如何工作的确切细节比这复杂一点.但它足以解决这个问题.这里的实际结果是你不能假设调用Collect也运行终结器,因为它没有.让我再说一遍:垃圾收集器的跟踪部分不运行终结器,Collect只运行收集机制的跟踪部分.
如果要保证所有终结器都已运行,请WaitForPendingFinalizers在调用后调用恰当命名Collect.这将暂停当前线程,直到终结器线程到处清空队列.如果你想确保那些最终确定对象有自己的内存回收,然后你将不得不叫Collect一个第二时间.
当然,不言而喻,您只应该为调试和测试目的而这样做.如果没有真正的理由,切勿在生产代码中做这些废话.
Ser*_*kov 15
实际上答案"这取决于".实际上有一个专用线程来执行所有终结器.这意味着GC.Collect只调用触发此过程并执行所有终结器将异步调用.
如果你想等到所有终结器被调用,你可以使用以下技巧:
GC.Collect();
// Waiting till finilizer thread will call all finalizers
GC.WaitForPendingFinalizers();
Run Code Online (Sandbox Code Playgroud)
stu*_*rtd 10
是的,但不是马上.此摘录来自垃圾收集:Microsoft .NET Framework中的自动内存管理(MSDN Magazine)(*)
"当应用程序创建一个新对象时,new运算符从堆中分配内存.如果对象的类型包含Finalize方法,则指向该对象的指针放在终结队列中.终结队列是一个内部数据结构控制垃圾收集器.队列中的每个条目都指向一个对象,该对象应该在回收对象的内存之前调用其Finalize方法.
当GC发生时......垃圾收集器扫描终结队列,寻找指向这些对象的指针.找到指针后,指针将从终结队列中删除并附加到可释放队列(发音为"F-reachable").可释放队列是由垃圾收集器控制的另一个内部数据结构.可分离队列中的每个指针都标识一个准备好调用其Finalize方法的对象.
有一个特殊的运行时线程专用于调用Finalize方法.当可释放队列为空(通常是这种情况)时,该线程会休眠.但是当条目出现时,此线程会唤醒,从队列中删除每个条目,并调用每个对象的Finalize方法.因此,您不应该在Finalize方法中执行任何代码,该方法对正在执行代码的线程做出任何假设.例如,避免在Finalize方法中访问线程本地存储."
(*)从2000年11月开始,事情可能会发生变化.
当收集垃圾时(无论是响应内存压力还是GC.Collect()),需要完成的对象都被置于终结队列中.
除非你打电话GC.WaitForPendingFinalizers(),否则终结器可能会在垃圾收集完成后很长时间内继续在后台执行.
BTW,也不能保证终结将被称为在所有.来自MSDN ......
Finalize方法可能无法运行完成,或者在以下特殊情况下可能根本不运行:
- 另一个终结器无限期地阻塞(进入无限循环,尝试获得它永远无法获得的锁等等).因为运行时尝试将终结器运行完成,所以如果终结器无限期地阻塞,则可能不会调用其他终结器.
- 该过程终止而不给运行时提供清理的机会.在这种情况下,运行时的第一个进程终止通知是DLL_PROCESS_DETACH通知.
只有在可终结对象的数量继续减少时,运行时才会在关闭期间继续完成对象.
| 归档时间: |
|
| 查看次数: |
12848 次 |
| 最近记录: |