在我的工作中,我们正在讨论清理大量托管的~50-100MB内存的不同方法。桌面上有两种方法(阅读:两位资深开发者不能同意)并没有经验团队的其他成员不确定哪种方法更可取,性能或可维护性。
收集的数据是许多小项目,约30000,其中包含其他项目,所有对象都被管理。这些对象之间有很多引用,包括事件处理程序,但不包括外部对象。我们将这一大组对象和引用称为一个名为blob的实体。
方法#1:确保切断对blob中对象的所有引用,并让GC处理blob和所有连接。
方法#2:在这些对象上实现IDisposable,然后对这些对象调用dispose并设置对Nothing的引用并删除处理程序。
第二种方法背后的理论是,因为较长寿命的物体在GC中需要更长的时间来清理。因此,通过将大型物体切割成更小的一口大小,垃圾收集器将更快地处理它们,从而提高性能。
所以我认为基本的问题是:拆分大量互连对象是否优化垃圾收集数据,或者最好将它们保持在一起并依靠垃圾收集算法为您处理数据?
我觉得这是一个预优化的案例,但我不知道GC是否足以知道它有什么帮助或阻碍它。
编辑:要强调内存的“blob”不是单个大对象,而是分别分配许多小对象。
如果它有用,可以多一点背景。我们有'泄漏',因为对象没有得到GCed。这两种方法都解决了泄漏问题,但在这一点上,这是一个更合适的辩论。
答案 0 :(得分:9)
第二种方法是错误的 - 它假定实现IDisposable
会影响垃圾收集器。
不幸的是, IDisposable与垃圾收集无关。它纯粹是关于发布非托管资源。听起来你的第二个高级开发人员试图为自己的利益做一点“太聪明”。
第一种方法应该没问题。一旦停止引用“blob”,博客中的每个对象都将无根,并且应该被清除。这可能在您发布引用后的某个不确定时间发生(除非您明确告知GC要收集,我不建议这样做)。相互依赖性将为您正确处理。
假设从理论上讲,实现IDisposable和清理内部引用可以加快收集过程。如果有(小)净收益,处理所有数据所花费的时间很可能超过GC中的任何收益 - 而且它实际上超出了您的业务关注。
然而,我怀疑它实际上会减慢整个垃圾收集器的速度,而不是加快速度。将数据集分解为大量对象无助于GC运行得更快 - 它仍然需要跟踪实时引用,这在这种情况下没有什么不同。
答案 1 :(得分:3)
这两种方法都没有意义。 GC在检测循环引用或复杂对象图时没有问题。设置对null的引用没有意义。 IDisposable没有改善GC性能。
如果您解决问题的方法有任何优势,那么将事件设置为null。如果它们是“向后”实现的话,它们具有保持引用对象的诀窍。换句话说:保持事件的发起者活着并拆除其客户。然后必须明确地取消订阅。
但是试图猜测这是错误的开始。任何像样的内存分析器都会向你显示什么参考使图表保持活着。
答案 2 :(得分:2)
IDisposable
接口与垃圾收集无关。
有些对象(如文件流)会占用宝贵的资源(因为进程的文件描述符限制通常远低于现代操作系统的内存限制)。但是,垃圾收集器不承认它们;因此,如果您的文件描述符不足但仍有足够的内存,垃圾收集器可能无法运行。
IDisposable
接口设置了一种机制,通过该机制,您可以放心,一旦对象实际变得无用,并且不仅在垃圾收集器决定运行时,将释放与托管对象关联的所有非托管资源。 / p>
因此,使对象IDisposable不会影响对象的垃圾收集方式。即使使用Dispose
方法清除所有引用,对垃圾收集器运行几乎没有影响;只需清除对blob的引用,就可以让所有较小的对象一次性无根。
答案 3 :(得分:1)
让GC做它的事情(当我输入这个时,会出现另外两个答案,说同样的话,非常多)。
答案 4 :(得分:1)
答案 5 :(得分:1)
方法#2:实施IDisposable 这些对象然后调用dispose 这些对象和设置引用 什么都没有,删除处理程序。
...
第二种方法背后的理论 是因为大的长寿 对象需要更长的时间来清理 GC。所以,通过切割大型物体 进入较小的一口大小 垃圾收集器将处理它们 更快,因此性能提升。
我认为这不是真的;垃圾收集器的成本通常取决于活动对象及其引用的数量,以及死对象的数量(取决于GC的类型)。一旦您不需要一个对象(或多个对象)并从根对象切割引用路径到它/它们,“垃圾”对象之间的引用数量无关紧要。所以,我会说,只要确保不会有来自“blob”之外的悬空引用,你就会好的。
答案 6 :(得分:0)
任何具有终结器的物体都应该在被遗弃之前尽可能地处置掉。从GC性能角度来看,使用终结器放弃对象应被视为最坏的情况。
除此之外,即使在没有终结器的情况下,也可以构建这样的场景,其中最好是简单地将一个大blob从世界其他地方分离并让它死掉,并且可以构建其中更好的场景。打破大对象。一般来说,简单地让大物体死亡是最佳的,除了两个警告,这可能有利于打破它:
我个人不喜欢在任何外部对象可能持有对发布者的引用的情况下放弃事件。主动清理似乎是一种更好的习惯。