基本概念
- 托管堆:在 C# 中,对象的内存分配主要发生在托管堆上。当创建一个对象时,CLR 会在托管堆上为其分配一块连续的内存空间。
- 引用计数:引用计数是一种简单的内存管理方法,它通过记录每个对象被引用的次数来判断对象是否可以被回收。当引用计数为 0 时,对象就可以被回收。但 C# 的 GC 并没有采用这种方法,而是使用了标记 - 清除和分代回收算法。
标记 - 清除算法
这是 GC 的核心算法之一,主要分为两个阶段:标记阶段和清除阶段。
标记阶段
- 根对象:GC 会从一组根对象开始,根对象是指那些始终可达的对象,例如全局变量、静态变量、当前执行方法的局部变量等。
- 标记过程:GC 会遍历所有的根对象,并递归地标记所有从根对象可达的对象。被标记的对象表示它们仍然在被使用,不能被回收。
清除阶段
- 清除过程:在标记阶段完成后,GC 会遍历整个托管堆,将所有未被标记的对象视为垃圾对象,并回收它们所占用的内存空间。
分代回收算法
C# 的 GC 采用了分代回收的策略,将对象分为不同的代(Generation),目前有三代:第 0 代(Gen 0)、第 1 代(Gen 1)和第 2 代(Gen 2)。
代的划分原则
- 第 0 代:新创建的对象通常被分配到第 0 代。这一代的对象生命周期较短,大部分对象在创建后很快就会变成不再使用的对象。
- 第 1 代:经过一次第 0 代垃圾回收后仍然存活的对象会晋升到第 1 代。第 1 代的对象生命周期相对较长。
- 第 2 代:经过多次第 1 代垃圾回收后仍然存活的对象会晋升到第 2 代。第 2 代包含一些长期存活的对象,如静态对象等。
分代回收的优势
- 提高回收效率:由于大部分新创建的对象很快就会变成垃圾,因此频繁地对第 0 代进行垃圾回收可以快速回收大量的内存空间,减少了垃圾回收的时间开销。
- 减少内存碎片:分代回收可以将生命周期不同的对象分开处理,减少了内存碎片的产生。
触发垃圾回收的条件
- 内存不足:当系统的可用内存低于某个阈值时,GC 会被触发,以回收不再使用的对象,释放内存空间。
- 特定代达到阈值:当某一代中的对象数量达到一定阈值时,会触发该代的垃圾回收。例如,当第 0 代中的对象数量达到阈值时,会触发一次第 0 代的垃圾回收。
- 手动调用
GC.Collect方法:在代码中手动调用System.GC.Collect方法来强制触发垃圾回收,但手动调用应该谨慎使用,因为可能会影响程序的性能。
示例代码
using System;
class Program
{
static void Main()
{
// 创建一些对象
for (int i = 0; i < 1000; i++)
{
object obj = new object();
}
// 手动触发垃圾回收
GC.Collect();
// 等待垃圾回收完成
GC.WaitForPendingFinalizers();
Console.WriteLine("垃圾回收已完成");
}
}
1517

被折叠的 条评论
为什么被折叠?



