我该如何使用垃圾回收器?

3

对于不了解GC并且从未使用过它(或者我认为没有这个需求)的人来说,它通常用于什么情况?如果我提高自己的技能并学习更多关于GC的知识,我或我的系统将如何受益?

更新 ...我如何让GC的工作更容易?


8
总之,不要这样做。 - Anthony Pegram
3
定义“更容易”。您对垃圾收集有问题吗?您能在您的问题中描述它吗? - spender
在大多数情况下,不要触碰.NET GC是正确的。这里有一个讨论特定情况下可能会有益的链接:http://blogs.msdn.com/b/ricom/archive/2004/11/29/271829.aspx - gooch
10个回答

14

通常情况下,使用垃圾回收的方法是不使用它,而是让CLR为您处理所有事情。


3

谢谢提供的链接,已经帮助我更好地理解了。 - stoic
2
在使用终结器有助于GC的情况下,是否存在任何情况?我认为使用终结器有助于GC,因为它不需要在终结/可达队列等上跟踪您的对象。 - LukeH

2

使用垃圾回收器的最佳方式是...

不要试图使用它!

让它自己运作。几乎每次人们尝试通过操作垃圾回收器来使其“更高效”,都会抑制它,并使其在工作中变得更糟。


使用GC的最佳方法是减少垃圾产生。 :-) 我经常看到一些天真的算法,当它们被稍微聪明一点的算法替换后,可以将垃圾产生量降低到原来的十分之一,并且运行速度会快几倍。现在的GC非常好,但是它们无法与首先减少工作量相匹配,我所见过的任何GC都不够聪明,无法识别低效的算法。 - Ken

1
GC(垃圾回收)与你在C++中定义的析构函数不同,也就是说你不需要定义它来释放之前分配的内存。GC的整个意义在于它是自动的。 我的建议是给我们更多关于你尝试做什么/理解什么的信息,因为这听起来并不安全。

2
C++ 中有哪些垃圾回收器? - Joe
就像提到的那样...我对GC一无所知,想知道更多关于它的好处 :) - stoic
@Joe - 我指的是自己实现的析构函数 ~MyObject()。 - JonH

1

为了使GC更容易,始终在支持IDisposable接口的任何对象上调用Dispose()。


3
GC与DisposeIDisposable完全无关。 - LukeH
不是直接的,但DisposeIDisposable是允许跳过需要终止器的类的机制,从而使垃圾收集器更容易处理。其他类应该没有问题... - Haas

1

编写代码时需要考虑垃圾回收的几个要点:

  1. 当您不再使用事件处理程序时,请始终确保取消注册。这是对象被保持活动状态远超其预期寿命的最常见方式,如果已释放的对象正在调用事件处理程序,则还可能导致错误。

  2. 如果您正在与非托管代码进行交互,则需要更加注意与非托管代码共享托管内存的情况。您可能需要使用固定和/或GC.KeepAlive来帮助GC了解您的非托管代码需要什么。尽量将固定保持在最小限度,因为这会使GC变得更加困难。

  3. 您几乎永远不需要实现终结器。如果类确实具有终结器,则应实现与IDisposable相同的清理,并在处理后调用GC.SuppressFinalize(this),因为这有助于GC有效地清理您的类。


0

如果你没有注意到你正在使用GC,那么你正在使用它,并且你正在正确地使用它。

只有当你错误地使用它时,才需要了解GC的内部工作原理。


PInvoke是一个明显的反例。即使在自包含的C#代码环境中,你仍然需要理解可到达性概念来编写不会泄漏的基于数组的数据结构。 - J D

0

了解它的工作原理总是有用的;但在大多数情况下,您不需要过于担心。

当您开始分配非托管资源(或具有此类资源的对象)时,值得阅读IDisposable pattern,以便您可以控制资源何时被释放(或者如果您想在与同行交谈时显得知识渊博,则称之为“确定性终结”)。


0
在通常使用C语言的内存管理风格中,跟踪堆中哪些区域是空闲的或已分配的信息是与指示哪些区域实际上被使用的信息分开的。当堆中的信息不再需要时,代码必须明确将其标记为未分配,否则它可能会永远保持已分配状态。
基于垃圾回收的系统将系统中所有堆对象引用视为指示哪些对象正在使用的权威指标。由于每次分配对象时扫描系统中所有对象引用是不切实际的,因此系统有效地“批处理”工作:只要堆上仍有可用的空闲内存空间,就会按顺序将内存分配给对象。在堆变得太满之前,不会尝试回收任何空间。

如果任何线程在本地变量中持有对对象的引用,或者任何全局变量持有对对象的引用,或者被认为是“已使用”的对象在任何字段中持有对它的引用,则将考虑该对象已经被“使用”。编译器通常可以判断一个持有对对象引用的本地变量是否实际上不会被使用,但它不能对全局变量或对象字段做出这样的判断。如果即将有用的对象持有一个从未再次实际使用的对象引用,则该引用应设置为null(在VB中为Nothing)。如果不这样做,无用的对象将与有用的对象同生共死。如果有用的对象像应用程序的主窗体这样,结果可能是一个持续存在的内存泄漏,只要应用程序保持打开状态。


这是关于1960年左右的GC的一个不错描述,但现在没有任何生产GC会像这样工作。增量和并发被用来避免批处理,因为它经常导致令人尴尬的长暂停。垃圾回收不再推迟到堆填满时进行,而是定期进行,重点放在最近分配的对象上。 - J D
虽然大多数现代垃圾收集系统使用分代技术,因此大多数收集不必检查所有内容,但这是一种实现细节。许多常见的垃圾收集器在停止-批处理模式下运行,是的,这有时会导致令人烦恼的暂停。理解的关键是,在GC系统中分配内存后,只有在GC发现不存在对其的根引用时,才能将其回收。 - supercat
通常无法显式地告诉垃圾收集器或分配器某个内存块将不再使用,因为分配器并不关心。当内存被分配时,分配器会从堆的末尾获取可用块(假设空闲块足够大),否则它将执行垃圾回收循环并希望能释放足够的空间。分配器不会尝试使用其他任何内存块来填充请求。 - supercat
分配器不会尝试使用其他内存块来填充请求。在这里的上下文中,C#和.NET实际上要么进行指针增量分配到gen0,要么在请求的大小足够大时将其分配到大对象堆中。 - J D
“其背后的推理完全超出了我的理解”。大对象堆(LOH)的创建是为了解决移动生成收集器在来回移动大型对象时浪费时间的问题。然而,LOH容易出现碎片化问题,我曾经遇到过激进回收大型对象导致32位机器出现内存不足错误的问题,即使还有足够的空间,因为它太过碎片化,.NET无法压缩它。 - J D
显示剩余2条评论

0
对于不了解GC并且从未有过使用它的需求(或者我认为没有),那么典型的使用场景是什么,如果我提升自己的技能并学习更多关于GC的知识,我/我的系统可以得到什么好处?
您可以利用垃圾回收的知识来提高您编写的软件的吞吐量和延迟。
更新...如何让GC更容易处理?
使用值类型来减少堆中指针的数量。使用对象树而不是长数组使堆遍历更加增量化并减少延迟。避免将引用写入堆中的可变对象,因为这会产生写障碍,并且会恶化吞吐量和延迟。

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