我的.NET Windows服务应用程序中存在内存泄漏问题。所以我开始阅读有关.NET中内存管理的文章。我在one of Jeffrey Richter articles找到了一个有趣的练习。这个练习名称是“对象复活”。它看起来像是将全局或静态变量初始化为“this”的位置代码:
protected override void Finalize() {
Application.ObjHolder = this;
GC.ReRegisterForFinalize(this);
}
我知道这是一种不好的做法,但我想知道使用这种做法的模式。如果您知道,请写在这里。
答案 0 :(得分:7)
来自同一篇文章:“复活的好用途很少,如果可能,你真的应该避免使用它。”
我能想到的最佳用途是“回收”模式。考虑一个生产昂贵的,几乎不可改变的物体的工厂;例如,通过解析数据文件,或通过反映程序集或深度复制“主”对象图来实例化的对象。每次执行这个昂贵的过程时,结果都不太可能改变。从头开始避免实例化符合您的最佳利益;但是,由于某些设计原因,系统必须能够创建许多实例(没有单例),并且您的消费者无法了解工厂,以便他们可以“返回”对象本身;他们可能会注入对象,或者获得一个工厂方法委托,从中获取引用。当依赖类超出范围时,通常也是实例。
一个可能的答案是覆盖Finalize(),清理实例的任何可变状态部分,然后只要Factory在范围内,就将实例重新附加到Factory的某个成员。这允许垃圾收集过程实际上“回收”这些对象的有价值部分,否则它们将超出范围并被完全破坏。工厂可以查看它的“垃圾箱”中是否有可用的回收物品,如果可以,它可以将其抛光并将其移出。如果进程使用的总对象数增加,工厂只需要实例化对象的新副本。
其他可能的用途可能包括一些高度专业化的记录器或审计实现,您希望在其死后处理的对象将自己附加到由此过程管理的工作队列。在该过程处理它们之后,它们可以完全被破坏。
一般来说,如果你想要家属思考他们是摆脱一个对象,或者不必打扰,但你想保留实例,复活可能是一个很好的工具,但你必须要看它非常谨慎地避免接收复活引用的对象成为“包装老鼠”的情况,并保留在进程的整个生命周期内在内存中创建的每个实例。
答案 1 :(得分:5)
推测:在Pool情况下,就像ConnectionPool一样。
您可以使用它来回收未正确处理但应用程序代码不再包含引用的对象。您不能将它们保存在池中的列表中,因为这会阻止GC收集。
答案 2 :(得分:4)
我的一个兄弟曾经在高性能仿真平台上工作过一次。他告诉我如何在应用程序中,对象构造是应用程序性能的一个明显瓶颈。看起来对象很大,需要一些重要的处理来初始化。
他们实现了一个对象存储库来包含“已退役”的对象实例。在构造新对象之前,他们首先检查存储库中是否已存在。
权衡是增加内存消耗(因为一次可能存在许多未使用的对象)以提高性能(随着对象构造的总数减少)。
请注意,实施此模式的决定是基于他们在特定方案中通过分析观察到的瓶颈。我希望这是一个特例。
答案 3 :(得分:3)
我唯一可以考虑使用此功能的地方可能是当您尝试清理资源时,资源清理失败。如果重试清理过程至关重要,那么从技术上讲,你可以“重新注册”要完成的对象,这有望成功,第二次。
话虽如此,我在实践中完全避免这种情况。
答案 4 :(得分:1)
据我所知,.net没有特定的顺序调用终结器。如果你的类包含对其他对象的引用,那么在调用终结器时它们可能已经完成(因此也就是Disposed)。如果您决定复活对象,则会引用最终/处置对象。
class A {
static Set<A> resurectedA = new Set<A>();
B b = new B();
~A() {
//will not die. keep a reference in resurectedA.
resurectedA.Add(this);
GC.ReRegisterForFinalize(this);
//at this point you may have a problem. By resurrecting this you are resurrecting b and b's Finalize may have already been called.
}
}
class B : IDisposable {
//regular IDisposable/Destructor pattern http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx
}