Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# GC.Collect()并完成_C#_.net_Garbage Collection_Finalizer - Fatal编程技术网

C# GC.Collect()并完成

C# GC.Collect()并完成,c#,.net,garbage-collection,finalizer,C#,.net,Garbage Collection,Finalizer,好的,众所周知,当GC将对象标识为垃圾时,它隐式地调用对象上的Finalize方法。但是如果我执行一个GC.Collect(),会发生什么呢?终结器是否仍在执行?也许是个愚蠢的问题,但有人问我这个问题,我回答“是”,然后我想:“这完全正确吗?”是的,但不是马上。本节选自(*) 当应用程序创建一个新对象时,新操作员分配 堆中的内存。如果对象的类型包含Finalize 方法,然后将指向该对象的指针放置在终结点上 最终确定队列是一个内部数据结构,受控制 队列中的每个条目都指向一个对象 应该在对象的内存

好的,众所周知,当GC将对象标识为垃圾时,它隐式地调用对象上的
Finalize
方法。但是如果我执行一个
GC.Collect()
,会发生什么呢?终结器是否仍在执行?也许是个愚蠢的问题,但有人问我这个问题,我回答“是”,然后我想:“这完全正确吗?”

是的,但不是马上。本节选自(*)

当应用程序创建一个新对象时,新操作员分配 堆中的内存。如果对象的类型包含Finalize 方法,然后将指向该对象的指针放置在终结点上 最终确定队列是一个内部数据结构,受控制 队列中的每个条目都指向一个对象 应该在对象的内存之前调用Finalize方法 可以回收

当GC发生时…垃圾收集器扫描终结 队列查找指向这些对象的指针。当找到指针时, 指针将从终结队列中删除并附加到 freachable队列(发音为“F-reachable”)。freachable队列为 垃圾收集器控制的另一个内部数据结构。 freachable队列中的每个指针都标识一个 准备好调用它的Finalize方法

有一个专用于调用Finalize的特殊运行时线程 方法。当freachable队列为空时(通常为 case),该线程处于休眠状态。但当条目出现时,该线程将被唤醒, 从队列中删除每个条目,并调用每个对象的Finalize 因此,您不应该在Finalize中执行任何代码 方法,该方法对执行 例如,避免访问 最终确定方法。”

(*)从2000年11月开始,所以从那时起情况可能已经发生了变化。

事实上答案是“视情况而定”。实际上,有一个专用线程执行所有终结器。这意味着对
GC.Collect
的调用只触发了这个过程,所有终结器的执行都将被异步调用

如果要等待调用所有终结器,可以使用以下技巧:

GC.Collect();
// Waiting till finilizer thread will call all finalizers
GC.WaitForPendingFinalizers();

当垃圾被收集时(无论是响应内存压力还是响应
GC.Collect()
),需要终结的对象被放入终结队列

除非调用
GC.WaitForPendingFinalizers()
,否则在垃圾收集完成后很长时间内,终结器可能会继续在后台执行


顺便说一句,根本不能保证会调用终结器。从

Finalize方法可能无法运行到完成,或者可能无法在运行时运行 在下列特殊情况下:

  • 另一个终结器无限期地阻塞(进入无限循环,尝试获取它永远无法获得的锁,依此类推)。因为 运行时尝试运行终结器以完成其他终结器 如果终结器无限期阻止,则可能不会调用
  • 进程终止时,运行时没有机会进行清理。在本例中,运行时的第一个进程通知 终止是DLL_进程_分离通知
运行时仅在关闭时才继续完成对象 可终结对象的数量继续减少

好的,众所周知,当GC将对象标识为垃圾时,它隐式地调用对象上的Finalize方法

不,不,不,这是未知的,因为为了获得知识,陈述必须是真实的。这种说法是错误的垃圾收集器在跟踪时不运行终结器,不管它是自己运行还是调用
Collect
终结器线程在跟踪收集器找到垃圾后运行终结器,这与调用
Collect
是异步的。(如果它真的发生了,正如另一个答案所指出的,它可能不会发生。)也就是说,在控件从
Collect
返回之前,您不能依赖于执行的终结器线程

下面是一个简单的工作原理示意图:

  • 当收集发生时,垃圾收集器跟踪线程跟踪根(已知为活动对象的对象,以及它们引用的每个对象等),以确定死对象
  • 具有挂起终结器的“死”对象将移动到终结器队列中终结器队列是根。因此,那些“死”的物体实际上仍然活着
  • 终结器线程(通常与GC跟踪线程不同)最终运行并清空终结器队列。然后,这些对象将真正失效,并在跟踪线程的下一个集合中收集。(当然,由于它们刚刚从第一个系列中幸存下来,它们可能是更高一代的。)
正如我所说,这太简单了;终结器队列如何工作的确切细节比这要复杂一些。但这已经足够让人理解这个想法了。这里的实际结果是,您不能假设调用
Collect
也会运行终结器,因为它不会。让我再重复一遍:垃圾收集器的跟踪部分不运行终结器,而
收集
只运行收集机制的跟踪部分

如果要确保所有终结器都已运行,请在调用
Collect
后调用适当命名的
WaitForPendingFinalizers
。这将暂停当前线程,直到终结器线程开始清空队列。如果您想确保这些最终确定的对象回收了内存,那么您必须再次调用
Collect

当然,不用说,你应该只做f