栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

.NET的GC垃圾回收机制的工作原理

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

.NET的GC垃圾回收机制的工作原理

.NET的GC垃圾回收机制的工作原理
  • 一、GC是什么
    • 1.1 为什么需要自动回收
    • 1.2 为什么需要释放内存
    • 1.3 什么是托管堆
  • 二、GC的工作原理
    • 2.1 托管堆的资源分配
    • 2.2 垃圾回收算法
    • 2.3 基于代的垃圾回收器的性能提升
    • 2.4 垃圾回收的触发条件
  • 三、其他

对学习的知识进行理解,消化,归纳。沉淀出自己的知识体系。第一次写博客,文中如果有错误的地方,希望能够帮忙指出修正,避免误导广大的人民群众,非常感谢!
如需要转载,请注明出处,谢谢!

一、GC是什么

GC,垃圾回收器,用于自动回收托管堆中无效对象的内存,释放内存空间。

1.1 为什么需要自动回收

(1)为什么需要自动回收内存,而不是让开发人员手动管理内存呢(例如C或者C++的开发人员),这是有原因的,接触过这两种开发语言的人估计都知道以下两点原因:

  1. 开发人员忘记释放内存导致内存泄漏。
  2. 试图使用已经释放的内存导致程序错误和安全漏洞。

这两种BUG比其他大多数的BUG都要严重,我们无法预测它们的后果或发生的时间。

1.2 为什么需要释放内存

(2)至于为什么需要释放内存这个就不用解释了吧,计算机的资源是有限的,如果要等到程序关闭的时候再去释放程序占用的内存,那么系统有再多的内存也不够用。程序在运行过程中 ,有很多对象分配内存空间后,使用一次就不再使用了,这不仅占用内存空间,导致内存溢出,还可能影响程序的性能。

1.3 什么是托管堆

(3)什么是托管堆。这个是引用类型对象的内存空间,可以这么说吧,值类型的内存一般在线程栈中,引用类型的内存在托管堆里。线程栈的内存按照对象的先进后出管理,而托管堆的内存是交给GC自动管理,我们一般不知道托管堆中的对象内存会在什么时候被释放。

二、GC的工作原理 2.1 托管堆的资源分配

CLR 要求所有对象都从托管堆分配。进程初始化时,系统分配一个地址空间区域作为托管堆,用一个指针NextObjPtr来指向下一个对象在堆中的分配位置,如图2-1


C#创建对象时,会执行以下步骤:

  1. 计算类型所需要的字节数。
  2. 加上每个对象都有的开销的字节数。每个对象都有两个开销字段:类型对象指针和同步块索引
  3. 检查托管堆中是否有足够的空间,如果有,则在指针NextObjPtr指向的地址放入对象,清零所分配的空间,调用类型构造器,new操作符返回对象引用。指针NextObjPtr移到下一个对象分配的地址。
2.2 垃圾回收算法
  1. 当GC进行垃圾回收时,首先会暂停进程的所有线程,防止GC在检查对象的时候被改变对象状态。
  2. GC会检查程序所有的变量,如果它指向的对象(堆)的同步索引标记为1,如果要被指向的对象的字段也有指向另一个对象,则另一个对象也会被标记为1,如果对象已被标记,则停止,避免死循环。
  3. 检查完毕后,托管堆中的对象要么被标记为1,要么没有被标记,没有被标记的对象就可以被GC进行回收。
  4. 托管堆的一些对象被删除后,会造成空间的碎片化问题(即不连续),这时GC会对托管堆进行压缩,其实就是重新对托管堆里面的对象进行移动,使之在地址上连续。如D对象前面的C对象被回收了,那么D对象的地址会向前移动C对象所占用的字节数。
  5. 托管堆中指针NextObjPtr重新指向最后一个幸存对象之后的位置
  6. 恢复应用程序的所有线程,GC工作完成。

2.3 基于代的垃圾回收器的性能提升

CLR 实际的GC是基于代的垃圾回收器,并不是每一次的垃圾回收都会检查托管堆里面所有的对象,它会对程序做了以下的假设:

  1. 对象越新,生存期越短。
  2. 对象越老,生存期越长。
  3. 回收堆的一部分,比回收整个堆快。

事实上我们的程序也正是如此,GC把托管堆分成三部分,分别是第0代、第1代和第2代,程序一开始没有经过GC回收的时候,程序内所有的对象均为第0代。第0代经过一次GC回收而没有被回收的对象升级为第1代,第1代经过一次GC回收而没有被回收的对象升级为第2代,最高代为第二代。

GC会优化只检查第0代的对象,当第1代跟第2代累积达到预算的时候才会检查回收(其他特殊情况例外)。
如图:

最分配F对象时,发现超出了第0代的预算,则启动GC回收。
回收第0代对象并进行压缩,调整第0代预算

达到第0代预算条件,继续进行第0代的回收,第1代对象不检查,加快了检查回收速度

回收第0代对象并进行压缩,调整第0代预算

达到第0代预算条件,未达到1代预算,继续进行第0代的回收,第1代对象不检查加快了检查回收速度

回收第0代对象并进行压缩,动态调整第0代预算

达到第0代预算条件,也达到1代预算,这次将检查第0代与第1代的所有对象进行回收。

以上演示了程序开始到产生第2代对象的过程,这只是一个演示的例子,实际上GC的代预算并没有这么小,达到第2代次数要比这个例子要多得多。

关于预算:这里只演示第0代的预算动态调整,其实第1代与第2代也是会进行启发式算法进行动态调整。调整原理:第0代:进行第0代回收时,如果发现没有多少内存被回收,则增加第0代的预算,减少回收频率;如果几乎所有的对象都是垃圾,则减少第0代的预算,增加回收频率,从而提升应用程序的总体性能。垃圾回收器检查有多少内存被回收,有多少幸存对象来动态调整预算。

大对象 :有另外的空间进行分配,并分配就默认为第2代。大对象不进行压缩,代价过高,未来版本可能压缩。
Markdown将文本转换为 HTML。

内存溢出:如果第0代没有回收到足够的内存分配给新对象,那么会执行一次完整的回收,如果还是不够,就抛出OutOfMemoryException异常。

2.4 垃圾回收的触发条件

正常情况下, CLR 检测达到第0代预算触发进行一次GC。但下列情况也会进行:

  1. 代码显示调用System.GC的静态Collect方法
  2. Windows报告低内存情况
  3. CLR正在卸载AppDomain
  4. CLR正在关闭
    不再试图压缩或释放内存,Windows将回收进程的全部内存。
三、其他

GC的一些其他内容,暂时还不知道怎么描述,以后有机会再作补充。

如终结的内部工作原理,其实就是Finalize方法对对象回收的一些影响。
如需要特殊清理的类型例如:System.IO.FileStream,等需要本机资源的类型。
如强制垃圾回收,就是对这个方法的一些讲解,也没有什么特殊的,我觉得就没有必要在这里说了。
如果想详细了解GC的工作原理,可以参考《CLR via C#》一书。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/910447.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号