C# 这是GC.ReRegisterForFinalize()的错误用法吗

C# 这是GC.ReRegisterForFinalize()的错误用法吗,c#,performance,garbage-collection,C#,Performance,Garbage Collection,所以我听说一般应该避免使用GC.ReRegisterForFinalize(),但我认为没有理由不对我的特定问题使用它。首先,这是一个对性能非常敏感的代码领域,所以速度很重要 我有一堆非托管资源和小的(8字节)托管对象,每个对象包装一个非托管资源。创建包装器时,它将非托管资源推送到堆栈上并包装它。当其被销毁时,而不是从堆栈中间移除资源(这会导致整个堆栈移动),它可以执行以下两种操作之一:要么将表示其堆栈位置的int推入队列,以便创建的下一个对象可以包装该堆栈位置,而不是推送新的堆栈位置,或者,它

所以我听说一般应该避免使用GC.ReRegisterForFinalize(),但我认为没有理由不对我的特定问题使用它。首先,这是一个对性能非常敏感的代码领域,所以速度很重要

我有一堆非托管资源和小的(8字节)托管对象,每个对象包装一个非托管资源。创建包装器时,它将非托管资源推送到堆栈上并包装它。当其被销毁时,而不是从堆栈中间移除资源(这会导致整个堆栈移动),它可以执行以下两种操作之一:要么将表示其堆栈位置的int推入队列,以便创建的下一个对象可以包装该堆栈位置,而不是推送新的堆栈位置,或者,它可以将自己推入队列,重新注册以完成并重新使用


差别可能很小,我意识到我可能在吹毛求疵,但至少在第二种方法中,每次发生时我都在保存分配和解除分配。那么缺点是什么呢?

好的,让我把这个写下来作为答案

GC.ReRegisterForFinalize
有两种用途-还原
GC.SuppressFinalize()
方法,以及“复活”已在其终结器中的对象(方法
~SomeType

您只需要了解最终确定是如何工作的。其基本思想是,当垃圾收集器收集非托管资源时,持有某些非托管资源的托管对象将有机会释放该非托管资源。由于在大多数情况下,您希望更早地释放资源(例如,释放
SqlConnection
,以确保不打开不必要的套接字),因此引入了优化,这是“一次性模式”的一部分

因此,终结器将包含摆脱非托管资源的代码,而不包含其他内容。为了处理早期处理,我们将实现一个
Dispose
方法,该方法进行一些清理,包括释放非托管资源。现在,该类型确实有一个终结器,因此它在默认情况下注册为终结器-但是,我们以前已经发布了这些非托管资源,所以在集合上进行终结没有意义。因此,您可以调用
GC.SuppressFinalize(this)
,这基本上会从要在收集时完成的项目列表中删除此实例

但是,如果您的类不仅仅是非托管资源的简单包装器,而是允许您再次释放和重新获取它,该怎么办
GC.ReRegisterForFinalize
起到了解救作用-无论何时重新获取非托管资源,您都将再次注册以完成,以确保正确的清理

在处理.NET中未更改的资源时,这有点违反了一个简单的原则,即托管包装器应该尽可能小和简单。因此,执行一些实际工作的实际类将使用这个小包装器,而不是直接使用非托管资源。这就是为什么您真的不需要
GC.ReRegisterForFinalize
,以及为什么在代码中看到它可能看起来可疑的原因之一

然而,
GC.ReRegisterForFinalize
的第二种用法更令人反感。它允许您停止实例的终结,并使其恢复生命。听起来不太糟吗?首先,这表明你的设计可能有缺陷。但更重要的是,可能已经收集了仅由该实例引用的所有其他实例(例如,指向另一个.NET对象的字段)。这是一个很好的方法来引入很难复制的bug。程序员不太喜欢这些,所以您主要是尽量避免这些


因此,TL;DR版本:您可能不需要
GC.ReRegisterForFinalize
。如果将托管包装器保存在“未使用”对象的列表中,则不会收集它,因此也不会最终确定它。只有先释放非托管资源(在
Dispose
中,包括对
GC.SuppressFinalize
的调用),然后希望为同一托管实例创建新的非托管资源,这才会有所不同。这可能不是你想要的(其实没什么意思)。

好吧,让我把这个写下来作为答案

GC.ReRegisterForFinalize
有两种用途-还原
GC.SuppressFinalize()
方法,以及“复活”已在其终结器中的对象(方法
~SomeType

您只需要了解最终确定是如何工作的。其基本思想是,当垃圾收集器收集非托管资源时,持有某些非托管资源的托管对象将有机会释放该非托管资源。由于在大多数情况下,您希望更早地释放资源(例如,释放
SqlConnection
,以确保不打开不必要的套接字),因此引入了优化,这是“一次性模式”的一部分

因此,终结器将包含摆脱非托管资源的代码,而不包含其他内容。为了处理早期处理,我们将实现一个
Dispose
方法,该方法进行一些清理,包括释放非托管资源。现在,该类型确实有一个终结器,因此它在默认情况下注册为终结器-但是,我们以前已经发布了这些非托管资源,所以在集合上进行终结没有意义。因此,您可以调用
GC.SuppressFinalize(this)
,这基本上会从要在收集时完成的项目列表中删除此实例

但是,如果您的类不仅仅是非托管资源的简单包装器,而是允许您再次释放和重新获取它,该怎么办
GC.ReRegisterForFinalize
起到了解救作用-无论何时重新获取非托管资源,您都将注册