📜  C#中的垃圾回收| .NET Framework

📅  最后修改于: 2021-05-29 19:50:29             🧑  作者: Mango

.NET Framework中的垃圾回收使自动内存管理成为可能。在运行时创建类对象时,会在堆内存中为其分配某些内存空间。但是,在程序中完成了与对象有关的所有动作之后,分配给它的存储空间就浪费了,因为无法使用它。在这种情况下,垃圾回收非常有用,因为它在不再需要时会自动释放内存空间。

垃圾回收将始终在托管堆上运行,并且在内部具有一个称为“优化引擎”的引擎

如果满足多个条件中的至少一个,则会发生垃圾回收。这些条件如下:

  • 如果系统的物理内存不足,则必须进行垃圾回收。
  • 如果分配给堆内存中各种对象的内存超过了预设阈值,则会发生垃圾回收。
  • 如果调用了GC.Collect方法,则会发生垃圾回收。但是,仅在正常情况下会调用此方法,因为正常情况下,垃圾收集器会自动运行。

垃圾收集阶段

垃圾收集主要分为三个阶段。有关这些的详细信息如下:

  1. 标记阶段:在标记阶段将创建所有活动对象的列表。这是通过遵循所有根对象的引用来完成的。不在活动对象列表中的所有对象都可能会从堆内存中删除。
  2. 重定位阶段:在重定位阶段,将更新所有活动对象列表中所有对象的引用,以便它们指向将在压缩阶段将对象重定位到的新位置。
  3. 压实阶段:随着释放死对象占用的空间并移动剩余的活动对象,堆会在压实阶段被压实。垃圾回收后剩余的所有活动对象均按其原始顺序移至堆内存的较旧端。

垃圾收集中的堆世代

堆内存分为3代,因此可以在垃圾回收期间适当地处理具有不同生存期的各种对象。取决于项目的大小,每一代的内存将由公共语言运行时(CLR)给出。在内部,Optimization Engine将调用Collection Means方法来选择哪些对象将进入第1代或第2代。

  • 第0代:所有短期对象(例如临时变量)都包含在堆内存的第0代中。除非它们是大对象,否则所有新分配的对象也是隐式的0代对象。通常,垃圾回收的频率在第0代中最高。
  • 第1代:如果运行在垃圾回收中未释放的第0代对象占用的空间,则这些对象将移至第1代。该代中的对象是第0代和第2代中的短期对象之间的一种缓冲区第2代中长期存在的对象
  • 第2代:如果某些第1代对象占用的空间未在下一次垃圾回收运行中释放,则这些对象将移至第2代。第2代对象的寿命很长,例如静态对象,因为它们保留在堆内存中在整个过程期间。

注意:一代的垃圾收集意味着其所有年轻一代的垃圾收集。这意味着该特定世代及其年轻世代中的所有对象均被释放。由于这个原因,第二代的垃圾收集被称为完全垃圾收集,因为释放了堆内存中的所有对象。同样,分配给第2代的内存将大于第1代的内存,并且类似地,第1代的内存将大于第0代的内存(第2代>第1代>第0代)。

给出了一个使用GC类的GC.MaxGeneration属性演示垃圾回收中堆生成数量的程序,如下所示:

using System;
  
public class Demo {
  
    // Main Method
    public static void Main(string[] args)
    {
        Console.WriteLine("The number of generations are: " +
                                           GC.MaxGeneration);
    }
}
输出:
The number of generations are: 2

在上面的程序中, GC.MaxGeneration属性用于查找系统支持的最大世代数,即2。如果要在联机编译器上运行此程序,则可能会得到不同的输出,因为它取决于系统。

GC类中的方法

GC类控制系统的垃圾收集器。 GC类中的一些方法如下:

GC.GetGeneration()方法:此方法返回目标对象的世代号。它需要一个参数,即目标代号,而该目标代号是必需的。

给出了一个演示GC.GetGeneration()方法的程序,如下所示:

using System;
  
public class Demo {
  
    public static void Main(string[] args)
    {
        Demo obj = new Demo();
        Console.WriteLine("The generation number of object obj is: "
                                          + GC.GetGeneration(obj));
    }
}
输出:
The generation number of object obj is: 0

GC.GetTotalMemory()方法:此方法返回系统中分配的字节数。它需要一个布尔参数,其中true表示该方法在返回之前等待垃圾回收的发生,false表示相反。

给出了一个演示GC.GetTotalMemory()方法的程序,如下所示:

using System;
  
public class Demo {
  
    public static void Main(string[] args)
    {
        Console.WriteLine("Total Memory:" + GC.GetTotalMemory(false));
  
        Demo obj = new Demo();
  
        Console.WriteLine("The generation number of object obj is: "
                                           + GC.GetGeneration(obj));
  
        Console.WriteLine("Total Memory:" + GC.GetTotalMemory(false));
    }
}
输出:
Total Memory:4197120
The generation number of object obj is: 0
Total Memory:4204024

注意:输出可能会有所不同,具体取决于系统。

GC.Collect()方法:可以使用GC.Collect()方法在系统中强制进行垃圾回收。此方法需要单个参数,即发生垃圾回收的最旧一代的数量。

给出了一个演示GC.Collect()方法的程序,如下所示:

using System;
  
public class Demo {
  
    public static void Main(string[] args)
    {
        GC.Collect(0);
        Console.WriteLine("Garbage Collection in Generation 0 is: "
                                          + GC.CollectionCount(0));
    }
}
输出:
Garbage Collection in Generation 0 is: 1

垃圾收集的好处

  • 垃圾回收使用几代垃圾回收成功地在堆内存上有效地分配了对象。
  • 不需要手动释放内存,因为垃圾回收会在不再需要时自动释放内存空间。
  • 垃圾回收可以安全地处理内存分配,因此没有对象会错误地使用另一个对象的内容。
  • 新创建的对象的构造函数不必初始化所有数据字段,因为垃圾回收会清除先前释放的对象的内存。