一个对象的垃圾回收,C#

4

我需要处理一个对象以释放它所拥有的一切,但它没有实现IDisposable,因此我不能在using块中使用它。我该如何让垃圾回收器将其收集?

6个回答

11
你可以使用GC.Collect()强制进行垃圾回收。但是请非常小心,因为完整的垃圾回收可能需要一些时间。最佳做法是让垃圾回收器自行决定何时进行垃圾回收。
如果对象包含未托管资源但未实现IDisposable接口,则这是一个bug。
如果没有未托管资源,则无论是否立即释放都不应该有影响,垃圾回收器应该会正确处理。

我认为他的问题是因为它包含托管资源,但使用了大量资源,所以当他不再需要该对象时,他希望它们能立即释放。 - DevinB
如果是这种情况,让垃圾回收器处理它。 如果资源足够大且施加了足够的内存压力,它将在下一个 0 代垃圾回收时处理。 - Michael
实际上,它持有一个套接字,我想尽快释放该套接字,但我没有关闭套接字的权限。 - Malfist
@Malfist:那么你需要更改对象,即使这意味着更换供应商。 - Joel Coehoorn
1
如果你所说的“供应商”是指“我为这个库付费的人”,那么正确的解决方案是放弃这个供应商,无论你对源代码能做什么。 - Lasse V. Karlsen
显示剩余2条评论

2
如果一个对象除了内存之外还“拥有”其他东西,你需要修复该对象以使用 IDisposable。如果这不是你所控制的对象,那么这就值得考虑选择另一个供应商,因为这反映出你的供应商到底有多么了解 .Net。
如果它只拥有内存,即使拥有大量内存,你只需确保对象超出范围即可。不要调用 GC.Collect() - 这是其中一件事情,如果你必须问,你就不应该这样做。

这个对象来自一个库,我无法控制它。它拥有一个套接字,我需要关闭并重新创建该套接字,但是在它持有套接字的时候我无法这样做,也无法访问该套接字。 - Malfist
解决方案#1,即您的快速修复:调用GC.Collect(); 解决方案#2,即长期(且正确)的解决方案:放弃该库并获取一个新的。 - Lasse V. Karlsen
1
#2并不总是一个选项 :) - alchemical

2
您不能对单个对象执行垃圾回收。您可以通过调用GC.Collect()请求垃圾回收,但这将影响所有需要清理的对象。这也是极不鼓励的,因为它可能会对后续回收的性能产生负面影响。
此外,调用Dispose并不清理对象的内存。它只允许对象删除对非托管资源的引用。例如,对StreamWriter调用Dispose会关闭流并释放Windows文件句柄。在随后的垃圾回收之前,所管理堆上对象的内存不会被回收。
Chris Sells在.NET Rocks上也讨论过这个问题。我认为这是在他的第一次出现时讨论的,但这个主题可能已经在后来的访谈中重新讨论过了。

http://www.dotnetrocks.com/default.aspx?showNum=10

这篇由Francesco Balena撰写的文章也是一个很好的参考资料:
《何时以及如何在C#中使用Dispose和Finalize》http://www.devx.com/dotnet/Article/33167/0/page/1

1

.NET 中的垃圾回收是非确定性的,这意味着您无法真正控制它何时发生。您可以建议,但这并不意味着它会听从。

告诉我们更多关于对象以及为什么您想要这样做的信息。我们可以根据此提出一些建议。代码总是有帮助的。根据对象的不同,可能会有一个 Close 方法或类似的方法。也许使用方式就是调用它。如果没有 Close 或 Dispose 类型的方法,您可能不想依赖该对象,因为如果它确实包含需要释放的资源,那么您可能会遇到内存泄漏问题。


0
如果对象超出范围并且没有外部引用,它将被相当快地收集(可能在下一次收集时)。

不一定。例如,如果已经移动到第二代,可能需要一段时间,如果有挂起的终结器,则需要进行两次收集。 - Brian Rasmussen

-5

请注意:在许多情况下,碎片化可能会带来问题,在处理大型对象(LOH 用于 ~80kb+ 的对象,执行无压缩并且针对许多常见用例存在高度的碎片化)时,GC.Collect() 或一些 IDisposal 并不是非常有用,这可能会导致内存不足 (OOM)问题,即使有数百MB可用。随着时间的推移,事物会变得越来越大,尽管对于被限制在 LOH 中的对象大小可能没有达到这个程度(80kb左右),高度的并行度加剧了这个问题,因为实例化/释放的对象数量更多,时间更短(而且大小可能会有所变化)。

数组是这个问题的罪魁祸首(由于运行时的非特定异常和断言,很难识别),建议对受此问题影响的代码实施积极的重用策略,如果能看到“大量大对象堆碎片”这样的信息就更好了。

并行扩展beta1示例中,Systems.Collections.Concurrent.ObjectPool类对于帮助一些项目重用对象(不幸的是我还没见过像附加属性/扩展方法这样简单普及的模式?)足够简单,可以轻松地投入使用或重新实现,只需分配一个生成器Func<>并使用Get/Put辅助方法来重用之前的对象和避免通常的垃圾回收。通常只需关注数组而非单个数组元素。

如果.NET 4更新所有.ToArray()方法以包括.ToArray(T target),那就太好了。

熟悉使用SOS/windbg (.loadby sos mscoreei逐步分析此类问题会有所帮助。考虑到目前的垃圾回收系统更像是垃圾再循环(再次使用相同的物理内存),ObjectPool类类似于垃圾重复利用。如果有人记得三R原则,减少内存使用也是提高性能的好方法;)


这个“答案”看起来更像是术语沙拉的展示品,而不是一个真正的答案。 - Blessed Geek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接