【问题标题】:C# - Preparing An Object For GC Before Calling GC.CollectC# - 在调用 GC.Collect 之前为 GC 准备一个对象
【发布时间】:2010-11-10 11:18:36
【问题描述】:

场景

  • 假设我已经决定我真的需要调用 GC.Collect();
  • 在实际调用此方法之前,我需要做些什么来确保对象已准备好进行适当的垃圾回收?
  • 将 null 分配给对象的所有属性就足够了吗?还是只是将 null 分配给对象本身?

如果你真的需要知道为什么.....

  • 我在 WCF 中有一个分布式应用程序,它每隔几秒通过网络发送一个 DataContract,其中包含 8 个字典作为 DataMembers。
  • 这是大量数据,当它进入客户端界面时,会创建一个全新的 DataContract 对象,并且应用程序的内存使用量增长得如此之大,以至于我遇到了 OutOfMemory 异常。

谢谢

编辑

感谢大家的cmet和回答,看来大家意见一致。

  • 我无法理解的是,由于连接一直处于打开状态,我如何才能正确处理。
  • 从传入的对象复制数据后,我不再需要该对象,那么只需在该 DataContract 对象上实现 IDisposable 就足够了吗?
  • 我原来的问题在这里 - Distributed OutOfMemory Exceptions

【问题讨论】:

  • "我遇到 OutOfMemory 异常" - 垃圾收集器被设计为在此之前自动启动。如果不是,那么您可能持有一些参考资料的时间比您需要的时间长。
  • 我怎么知道我持有参考资料?
  • 因为它没有被收集(我承认这听起来有点鸡/鸡蛋)

标签: c# garbage-collection


【解决方案1】:

只要没有其他东西可以看到该对象,它就已经有资格被收集;不需要更多。这里的关键是确保没有其他人在观看它(或者至少没有生命周期更长的东西):

  • 是在某地的某个地方吗?
  • 是否在不完整的方法中的变量中? (可能是无限循环或迭代器块)
  • 它是否在某个集合中?
  • 它订阅了某个事件吗?
  • 它被捕获在一个仍然存在的闭包(lambda / anon-method)中?

我真的怀疑GC.Collect() 是这里的答案;如果它符合条件,它就已经被收集了。如果它可以理解,那么调用GC.Collect() 肯定不会有任何帮助,而且很可能会使事情变得更糟(在无法收集到任何有用信息时占用 CPU)。

【讨论】:

  • 显式调用 GC.Collect 的真正问题是将对象推送到后代,这意味着您实际上是在延迟它们的收集。
  • GC已经在开箱即用的情况下进行了调整。如果您收集的频率比您需要的多(即明确地),您将更快地将对象推送到更高代。您收集的次数越多,符合收集条件的物品数量就越少,获得无根据促销的物品数量就越多。您很少能比 GC 更聪明。我很想有一个 GC.CollectButDontPromote 方法,但由于它不存在,除非分析告诉您您的明确收集有帮助,否则我会说不要这样做。
【解决方案2】:

你通常不需要做任何事情。

如果该对象不再被引用,则它是收集的候选对象。 (相反,如果对象仍然被引用,那么它不是收集的候选对象,但是你“准备”它。)

【讨论】:

    【解决方案3】:

    您需要清理所有非托管资源,例如数据库连接等。
    通常通过实现IDisposable 并调用Dispose

    如果您有终结器,您应该致电GC.SuppressFinilize

    其余的由垃圾收集器清理。

    编辑:
    而且,哦,当然你需要释放对你的对象的所有引用。

    但是,这里就是这么大的但是。除非您有非常非常特殊的情况,否则您不需要调用 GC.Collect。您可能忘记释放一些资源或引用,而 GC.Collect 不会帮助您。确保在所有 Disposable 上调用 Dispose(最好使用 using 模式)。

    您可能应该拿起像Ants memory profiler 这样的内存分析器,看看您的所有内存都去了哪里。

    【讨论】:

    • 从传入对象复制数据后,我不再需要该对象,因此只需在该 DataContract 对象上实现 IDisposable 就足够了吗?
    • @Goober 否。具有 DataContract 的类型不包含任何托管资源,那么您不需要实现 IDisposable。除非您将其存储在其他地方,否则一旦您使用 OperationContract 离开该方法,它将由 GC 收集。
    • @Goober 试用 Ants 内存分析器,您可以免费试用 30 天左右。它将帮助您确定哪些对象保留在内存中以及它们仍然被引用的位置。
    • @Goober,没必要。 IDisposable 只能摆脱对象本身控制的引用,如果它订阅了事件,它可以要求取消订阅,但是如果带有事件的对象没有正确实现它的删除,它可能仍然持有对你的引用。此外,lambdas 可能会让人感到沮丧。有两件事可以提供帮助,WINDBG 可以告诉你什么是对什么的引用。反应式扩展可用于反转使用集合的代码,并减少内务处理代码......
    • 原来是一些非常愚蠢的东西,感谢 ANTS Memory Profiler。非常感谢。
    【解决方案4】:

    如果您不再直接引用某个对象,并且内存不足,GC 应该会自动执行此操作。请确保在数据上下文中调用 .Dispose()。

    【讨论】:

      【解决方案5】:

      调用GC.Collect 几乎不会阻止您获得OutOfMemory 异常,因为当.NET 由于OOM 无法创建新对象时,它会调用GC.Collect 本身。我只能想到一种情况,那就是当您在可终结队列中注册了未引用的对象时。当这些对象引用许多其他对象时,可能会导致 OOM。这个问题的解决方案实际上不是调用GC.Collect,而是确保正确释放这些对象(并在创建这些对象时正确实现释放模式)。

      【讨论】:

        【解决方案6】:

        一般使用 GC.Collect

        因为你试图摆脱一个非常大的集合,所以使用 GC.Collect() 是完全有效的。来自Microsoft docs

        ...由于您的应用程序比运行时更了解其行为,因此您可以通过显式强制一些集合来帮助解决问题。例如,您的应用程序在用户保存他的数据文件后强制所有代的完整收集可能是有意义的。

        “准备”您的对象

        来自优秀的Performance Considerations for Run-Time Technologies in the .NET Framework(来自MSDN):

        如果你保留一个指向资源的指针,GC 将无法知道你是否打算在未来使用它。这意味着您在本机代码中用于显式释放对象的所有规则仍然适用,但大多数时候 GC 会为您处理所有事情。

        因此,为确保它已准备好进行 GC,请确保您没有对要收集的对象的引用(例如,在集合、事件等中)。将变量设置为 null 意味着它已准备好在变量超出范围之前进行收集。

        任何实现 IDisposable 的对象都应该调用它的 Dispose() 方法来清理非托管资源。

        使用 GC.Collect 之前

        由于您的应用程序看起来像是服务器,因此使用服务器 GC 可能会解决您的问题。在多处理器场景中,它可能会更频繁地运行并且性能更高。

        服务器 GC 旨在实现最大吞吐量,并以非常高的性能进行扩展。

        请参阅Performance Considerations for Run-Time Technologies in the .NET Framework(来自 MSDN)中的选择要使用的垃圾收集器

        【讨论】:

          猜你喜欢
          • 2011-06-08
          • 1970-01-01
          • 1970-01-01
          • 2019-04-05
          • 2016-04-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-17
          相关资源
          最近更新 更多