基于C#了解垃圾回收机制

打印 上一主题 下一主题

主题 1030|帖子 1030|积分 3090

1. 什么是垃圾回收(Garbage Collection,简称GC)
在C#等支持自动内存管理的编程语言中,垃圾回收机制是一种自动释放不再被步伐使用的内存空间的机制。
步伐运行过程中会不停创建对象实例,这些对象占用内存空间,而当某些对象不再被需要时(比如其对应的引用变量不再指向它,且步伐后续也不会再访问到它),就需要回收它们所占用的内存,以便内存能被高效地重复使用,制止内存走漏等题目。

2. 垃圾回收的工作原理
C#的垃圾回收器基于引用计数和跟踪算法来实现内存管理。当一个对象没有任何引用指向它时,垃圾回收器就会认为这个对象是不再需要的,从而将其占用的内存回收。
C#的垃圾回收器紧张包括三个代(Generation):第0代、第1代和第2代。差异代的对象具有差异的生命周期和回收频率。第0代包罗新创建的对象,第1代包罗从第0代晋升的对象,第2代包罗从第1代晋升的对象以及长期存活的对象。
3. 垃圾回收的工作流程


  • 标记阶段:
  • 垃圾回收器会从一组称为“根”(Roots)的对象开始遍历,这些“根”通常包括全局变量、静态变量、当前正在执行的方法的局部变量(在栈上的引用)等。从这些“根”出发,沿着对象引用关系链进行遍历,能被访问到的对象会被标记为“可达”(Reachable),而那些无法通过“根”对象引用链访问到的对象就被认为是“不可达”(Unreachable),也就是可以被回收的垃圾对象。例如,在一个方法中创建了一个局部对象,方法执行竣事后,假如没有其他地方再引用这个对象,那么从“根”出发就无法访问到它了,它就会在这个阶段被标记为不可达。
  • 扫除阶段:
  • 在标记出所有的不可达对象后,垃圾回收器会回收这些对象所占用的内存空间,使这些内存可以重新被分配给新创建的对象使用。差异的垃圾回收算法在具体的回收内存方式上会有所差异,比如有的是直接释放内存,有的大概会进行内存整理(将存活的对象移动到一起,便于后续内存分配更加连续高效等操作)。
4. 常见的垃圾回收算法



  • 标记-扫除算法(Mark-Sweep):
  • 这是最基础的一种算法,先标记出所有需要回收的对象(不可达对象),然后统一回收它们占用的内存空间。缺点是会产生内存碎片,由于回收后的内存空间大概是不连续的小块,倒霉于后续分配较大对象时的内存申请。
  • 标记-整理算法(Mark-Compact):
  • 在标记阶段类似标记-扫除算法,标记出不可达对象后,会将所有存活的对象(可达对象)向一端移动,然后直接清理掉边界之外的内存空间,这样可以制止内存碎片题目,但移动对象会有肯定的性能开销。
       
                   
                                            主流的"标记并扫除"的方式


  • 复制算法(Copying):
  • 它将可用内存划分为两块巨细相称的区域,每次只使用此中一块。当进行垃圾回收时,把存活的对象从正在使用的这块区域复制到另一块空闲区域,然后直接把原来使用的区域全部清理掉。长处是简单高效且不会产生内存碎片,缺点是可用内存空间使用率不高,始终只能使用一半的内存。
  • 引用计数算法 :
  • 每个对象都有一个引用计数器,当有新的引用指向对象时,计数器增加;当引用脱离作用域或被释放时,计数器淘汰。当计数器为0时,对象被视为不再需要,可以被回收。


代码示例来观察垃圾回收的结果(简单表现)
以下是一个简单的C#代码示例,通过创建一些对象并让它们的引用关系变革,来观察垃圾回收的环境(实际中很难准确控制垃圾回收器何时执行,这里只是模拟一种大概触发垃圾回收的场景)。
  1. using System;
  2. class MyClass
  3. {
  4.     ~MyClass()
  5.     {
  6.         Console.WriteLine("对象被回收了");
  7.     }
  8. }
  9. class Program
  10. {
  11.     static void Main()
  12.     {
  13.         // 创建一个对象实例
  14.               MyClass obj1 = new MyClass();
  15.         // 再创建一个对象实例
  16.               MyClass obj2 = new MyClass();
  17.         // 让obj2不再引用它原来指向的对象,模拟失去引用的情况
  18.         obj2 = null;
  19.         // 强制进行垃圾回收(虽然不能保证立刻执行,但会提示垃圾回收器可以尝试回收了)
  20.         GC.Collect();
  21.         GC.WaitForPendingFinalizers();
  22.         // 让obj1也不再引用对象,再次模拟失去引用情况
  23.         obj1 = null;
  24.         // 再次强制进行垃圾回收
  25.         GC.Collect();
  26.         GC.WaitForPendingFinalizers();
  27.         Console.ReadKey();
  28.     }
  29. }
复制代码
输出结果:
  1. 对象被回收了!
  2. 对象被回收了!
复制代码
在上述代码中:


  • 首先定义了一个 MyClass 类,并且重写了析构函数(~MyClass()),在析构函数中输出一条表现对象被回收的信息,这样当对象被垃圾回收时就能看到相应提示。
  • 在 Main 方法中,先创建了 obj1 和 obj2 两个 MyClass 对象实例,然后将 obj2 赋值为 null,此时原来 obj2 指向的对象就失去了一个引用,通过调用GC.Collect()(自动发起垃圾回收)和GC.WaitForPendingFinalizers()(等待闭幕器执行完毕,析构函数在这里属于闭幕器相关操作)来尝试触发垃圾回收,看看是否会回收那个失去引用的对象(假如符合回收条件的话)。之后又将 obj1 也赋值为 null,再次进行垃圾回收相关操作,观察对象被回收的环境。
                                                   代码思想也是泉源于生存
需要注意的是,在实际的生产代码中,一样平常很少会自动去频繁调用 GC.Collect(),由于垃圾回收器本身有自己的一套优化战略和触发机制来决定何时进行垃圾回收最合适,过分手动干预大概反而会影响步伐的性能和正常运行逻辑。
别的,差异版本的.NET框架以及运行时环境下的垃圾回收机制在细节和实现上大概会有肯定差异,本文只是先容了根本的通用原理和简单的示例演示。




免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

老婆出轨

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表