C# .NET 垃圾回收入门指南






4.75/5 (35投票s)
这是一篇关于 C# .NET 垃圾回收的入门指南。
在 .NET 开发领域,垃圾回收(Garbage Collection)常常被视为一种“魔法”,尤其是对于初级程序员来说。在构建大多数应用程序时,并不需要真正理解它是如何工作的,因此它就成了一个谜,直到你觉得有必要去了解它,或者觉得它很有趣去学习。我记得在我职业生涯早期,我所在的一个团队面临一个由于内存泄漏导致性能问题。我们决定通过在代码中到处添加 `GC.Collect` 调用来尝试解决,看看是否能起作用。现在回想起来,我感到一阵懊恼。这是一个典型的“碰运气”的例子,而我们当时本应该花更多时间去尝试理解和诊断问题。至少,对垃圾回收有一个基本的了解,可以防止你以这种方式尝试解决性能问题,而不知道调用 `GC.Collect` 实际上在做什么。
C# .NET 中的垃圾回收是什么?
垃圾回收是一个自动化的过程,用于释放(或称为解除分配)应用程序不再需要的对象所占用的内存。大多数开发者至少知道这一点。
每次在 .NET 中实例化一个对象时,都会分配一部分内存来存储该对象。但到了某个时候,你的应用程序可能不再需要那个对象了。只有通过解除分配其内存,它才能再次被你的应用程序重新利用。如果内存没有被解除分配,那么迟早你的应用程序会耗尽内存而停止工作。
所以垃圾回收是必须的吗?
不,垃圾回收只是解决如何解除分配内存问题的一种方法。在某些语言中,比如 C 和 C++,程序员需要负责跟踪哪些对象不再需要,并根据需要显式地解除分配内存。
垃圾回收是如何工作的?
在 CLR(公共语言运行时)内部,存在一个“垃圾回收器”。不出所料,垃圾回收器的职责就是管理垃圾回收。它通过定期执行“垃圾回收”来完成这项工作。每一次执行垃圾回收时,垃圾回收器都会检查内存(即应用程序的 托管堆),并释放不再需要的内存,也就是被“死对象”占用的内存。
它如何知道对象何时是“死的”?
如果一个对象无法被你的代码访问,那么它就是“死”的。最明显的例子是方法内的局部变量。一旦方法返回,你的代码就无法访问该变量,所以它就变成了“死的”。
垃圾回收器多久执行一次垃圾回收?
有三种方式可以触发垃圾回收。
第一,如果你的系统物理内存不足,这可以触发一次垃圾回收。
第二,会定义一个阈值,表示堆上可供已分配对象使用的内存的可接受水平。如果超过了这个阈值,就会触发一次垃圾回收。
最后,可以通过调用 `GC.Collect` 方法显式地触发垃圾回收。只有在极少数情况下才需要这样做。
那么,除了调用 GC.Collect 的情况,垃圾回收基本上是在垃圾回收器认为释放一些内存可能有用时触发的,对吗?
是的。
那么,无论何时触发垃圾回收,它都会释放堆上所有被死对象占用的内存吗?
不,因为扫描整个托管堆来查找死对象可能需要很长时间,从而影响性能。每次触发垃圾回收时,所有其他线程的执行都会暂停,直到它完成。所以垃圾回收器会尝试以高效的方式查找和解除分配死对象。它是选择性的。
它是如何选择性的?
基本上,堆上的每个对象都被归类到三个“代”之一。这些被称为“第 0 代”、“第 1 代”和“第 2 代”。一个对象的代表示它的“年龄”,也就是它创建以来已经有多长时间了。总的来说,第 0 代是为年轻对象准备的,第 1 代是为中年对象准备的,第 2 代是为年长对象准备的。
当发生垃圾回收时,它会针对特定的代进行,并解除分配该代以及所有更年轻代中的死对象。所以,对第 1 代的回收会解除分配第 0 代和第 1 代中的死对象。唯一一次能够解除分配堆上 *所有* 死对象的情况是,对第 2 代进行回收。
垃圾回收通常在第 0 代进行,也就是针对短命对象。这是基于一个合理的假设,即最有可能成为“死对象”的是那些最近分配的对象。
如果一个对象在垃圾回收时“存活”下来,它就会被“提升”到下一代。所以,在垃圾回收时仍然存活的第 0 代对象会被提升到第 1 代。这里的假设是,如果它在这之后仍然存活,那么很有可能在下一次垃圾回收后仍然存活,所以我们将其移出我们“最高优先级”的第 0 代回收。
那么新对象presumably 会被分配到第 0 代,对吗?
当一个新对象被分配时,它会被放入第 0 代,但有一个例外。大对象(大于 85,000 字节)会直接进入第 2 代。这个决定是基于一个假设,即大对象更有可能是长寿的。
…到此为止,关于垃圾回收的基础知识就差不多了。
正如我们所见,垃圾回收器会根据对你的应用程序的一些假设来决定如何行为。只有当这些假设不适用于你的应用程序时,你才需要考虑对其进行操作的可能性。例如,你可以配置垃圾回收器的“侵入性”(触发频率),或者显式地触发对特定代的回收。
许多开发者从未觉得有必要理解垃圾回收是如何工作的,这或许表明它做得相当好。垃圾回收器为你做了那些你不得不做的工作。
这篇博文 C# .NET 垃圾回收入门指南 最初发表在 The Proactive Programmer。