C# 物体复活的用法

C# 物体复活的用法,c#,memory-management,clr,C#,Memory Management,Clr,我的.NET Windows服务应用程序存在内存泄漏问题。所以我开始阅读有关.NET内存管理的文章。我发现了一个有趣的实践。这种做法的名称是“对象复活”。它看起来像是将全局或静态变量初始化为“this”的代码: 我知道这是一种不好的做法,但是我想知道使用这种做法的模式。如果您知道,请写在这里。我能想到的唯一可能使用此功能的地方是当您尝试清理资源时,而资源清理失败。如果重试清理过程非常关键,那么从技术上讲,您可以“重新注册”要最终确定的对象,这有望第二次成功 话虽如此,我还是会在实践中完全避免这种

我的.NET Windows服务应用程序存在内存泄漏问题。所以我开始阅读有关.NET内存管理的文章。我发现了一个有趣的实践。这种做法的名称是“对象复活”。它看起来像是将全局或静态变量初始化为“this”的代码:


我知道这是一种不好的做法,但是我想知道使用这种做法的模式。如果您知道,请写在这里。

我能想到的唯一可能使用此功能的地方是当您尝试清理资源时,而资源清理失败。如果重试清理过程非常关键,那么从技术上讲,您可以“重新注册”要最终确定的对象,这有望第二次成功

话虽如此,我还是会在实践中完全避免这种情况。

来自同一篇文章:“复活很少有好的用途,如果可能的话,你真的应该避免它。”

我能想到的最好的用途是“回收”模式。考虑一个生产昂贵、几乎不变的物体的工厂;例如,通过解析数据文件、反映程序集或深度复制“主”对象图实例化的对象。每次执行此昂贵的过程时,结果不太可能改变。避免从头开始实例化符合您的最佳利益;但是,由于某些设计原因,系统必须能够创建多个实例(没有单例),并且您的消费者无法了解工厂,因此他们可以自己“返回”对象;它们可以被注入对象,或者被赋予一个工厂方法委托,从中获取引用。当依赖类超出范围时,通常实例也会超出范围

一个可能的答案是重写Finalize(),清除实例的任何可变状态部分,然后只要工厂在范围内,就将实例重新附加到工厂的某个成员。这实际上允许垃圾收集过程“回收”这些对象中有价值的部分,否则它们将超出范围并被完全销毁。工厂可以查看它的“垃圾箱”中是否有可回收的物品,如果有,它可以将其抛光并分发出去。如果进程使用的对象总数增加,工厂只需实例化对象的新副本

其他可能的用途可能包括一些高度专业化的记录器或审计实现,在这些实现中,您希望在对象死后处理的对象将自身附加到由该进程管理的工作队列。在流程处理它们之后,它们可以被完全销毁


一般来说,如果您希望从属对象认为他们正在摆脱某个对象,或者不必麻烦,但希望保留该实例,则复活可能是一个很好的工具,但您必须非常仔细地观察它,以避免接收复活引用的对象成为“打包老鼠”的情况并在进程的生命周期内保留在内存中创建的每个实例。

推测性:在池的情况下,如ConnectionPool


您可以使用它来回收未正确处置但应用程序代码不再持有引用的对象。您不能将它们保存在池中的列表中,因为这样会阻止GC收集。

我的一个兄弟曾经在一个高性能模拟平台上工作过。他向我讲述了在应用程序中,对象构造是应用程序性能的明显瓶颈。看起来对象很大,需要一些重要的处理来初始化

他们实现了一个对象存储库来包含“失效”的对象实例。在构建一个新对象之前,他们首先要检查存储库中是否已经存在一个对象

取舍是增加内存消耗(因为一次可能存在许多未使用的对象)以提高性能(因为对象构造的总数减少)


请注意,实现此模式的决策是基于他们在特定场景中通过分析观察到的瓶颈。我希望这是一个例外情况。

据我所知,.net没有按特定顺序调用终结器。如果类包含对其他对象的引用,则在调用终结器时,这些对象可能已被终结(并因此被释放)。如果您随后决定恢复您的对象,您将拥有对最终确定/处置对象的引用

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
}
A类{
静态集合重新选择A=新集合();
B=新的B();
~A(){
//不会死的。在resurectedA中保留一个参考资料。
增加(这个);
GC.重新登记以最终确定(本);
//在这一点上,你可能有一个问题。通过复活这个,你正在复活b,b的终结可能已经被调用。
} 
}
B类:IDisposable{
//常规IDisposable/Destructor模式http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx
}

如果是为我工作的开发人员写的代码,我会让他们把它撕掉。@John:我完全同意-这确实是极端边缘情况下的事情,依我看。@John:我清楚地理解它,我的兴趣只是科学:)就是这么说的。注意在“real”C中的符号
protected override void Finalize(){…}
不允许用于此特定覆盖。对于终结器,必须使用
~NameOfClass(){…}
。当计数器降至0时,必须抑制最后一次终结,并且对象正在消亡。但无论如何,我认为这并不是一个好的池的实现方式。@Vokin,反策略并不是这里管理生活的唯一方法。我认为主要的一点是从GC中回收资源。对象回收可以(而且可能应该)在没有复活的情况下完成。一个更大的用例,我想我
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
}