CLR中的垃圾回收器

托管堆:
在每个程序中都要使用资源,包括文件、内存缓冲区、网络连接、数据库资源等等。在面向对象中,每个类型都代表可供程序使用的一种资源。要使用资源,必须为代表资源的类型分配内存。那访问一个资源需要哪些步骤呢?
1.调用IL指令newobj,为代表资源分配内存
2.初始化内容,设置资源的初始状态并使资源可用。
3.访问类型的成员来使用资源
4.摧毁资源的状态以进行清理。
5.释放内存。

原生的C++开发人员就需要手动管理内存,如果开发者忘记释放不在需要的内存而造成内存泄漏,或者在试图使用已经释放的内存,然后由于内存被破坏而造成程序错误和安全漏洞。

C# 只需要写的是可验证的、类型安全的代码,应用程序就不可能会出现内存被破坏的情况。
托管堆的作用就是为开发人员提供一个简化的编程模型:分配并初始化资源并直接使用。大多数类型都无需资源清理,垃圾回收器会自动释放内存。

从托管堆分配资源:
在进程初始化时,CLR划出一个地址空间区域作为托管堆。另外CLR还需要维护一个指针,该指针指向下一个对象在堆中的分配位置,默认为地址空间区域的基地址。
应用程序的内容受进程的虚拟地址空间限制,32位进程最多能分配1.5GB,64位进程最多能分配8TB。

分配流程:
1.C#使用new字段,计算类型的字段(包含从基类类型继承的字段)所需的字节数。
2.加上对象的类型对象指针和同步块索引。对32位应用程序,这个两个字段各自需要32位。对于64位应用程序,这个两个字段需要64位。
3.CLR检查区域中是否有分配对象所需的字节数。有足够的可以用空间,就在维护的指针指向的地址放入对象。为对象分配的字节会被清零。并调用类型的构造器,new操作符返回对象引用。在返回引用之前,会指针会加上对象占用的字节数来得到一个新值。即下个对象放入托管堆时的地址。

垃圾回收(GC)算法:
CLR使用一种引用跟踪算法。该算法只关心引用类型的变量,因为只有这种变量才能应用堆上的对象。值类型变量直接包含值类型实例。
引用类型的变量可在许多场合使用,包括类的静态和实例字段,或者方法的参数和局部变量,我们将所有引用类型的变量都称为根。

CLR执行GC流程:
1.暂停进程中所有线程,防止线程在CLR检查期间访问对象并更改状态。
2.CLR进入GC标记阶段:

1.遍历堆中所有对象,将同步块索引字段中的一位设为0,表示所有对象都应删除
2.CLR检查所有活动根,查看它们引用了哪些对象,如果一个根包含null,CLR忽略这个根并继续检查下个根。任何根引用了堆上的对象,CLR都会标记那个对象,也就是将同步块索引中的一位设为1.已标记对象是可达的,所以不能被垃圾回收,未标记对象是不可达的,因为应用程序中不存在使对象能被再次访问的根。

3.标记完成后,进入GC压缩阶段:

1.CLR对堆中已标记的对象进行压缩,他们占用连续的内存空间。优点:搜点恢复了引用的“局部化”,减少应用程序的工作集,提升了将来访问这些对象时的性能,解决本机堆的空间碎片化问题。
2.CLR从每个根减去所引用的对象在内存中偏移的字节数,重新制定根所引用的对象在堆中的位置。

4.设置指针指向最后一个对象之后的位置,然后恢复应用程序的所有线程,这些线程继续访问对象。

如果CLR在一次GC之后回收不了内存,而且进程中没有空间来分配新的区域,说明该进程内存已耗尽。会抛出OutOfMemoryException异常。

注意:
一但根离开作用域,它引用的对象就会变得“不可达”,GC会回收其内存

代:提升性能
CLR的GC是基于代的垃圾回收器,它对代码做出了几点假设:
1.对象越新,生存期越短。
2.对象越老,生存期越长。
3.回收堆的一部分,速度快于回收整个堆。

代的工作原理:
第0代对象:托管堆的初始化时是不包含对象的。那些新构造的对象,垃圾回收器从未检查过的对象,称为第0代对象。

CLR初始化时为第0代对象选择一个预算容量(kb为单位),如果分配一个新对象造成第0代对象超过预算,就必须启动一次垃圾回收。

第一代对象:在第0代空间满员后,再次添加的分配的对象,会启动一次垃圾回收,未被销毁对象在GC压缩后成为第一代对象。

在经历过垃圾回收后,第0代就不包含任何对象。和前面一样,新对象会分配到第0代对象中。再次进行垃圾回收时,会检查第1代占用了多少内存,如果第一代占用内存少于预算容量,所以垃圾回收只检查第0代对象。

第二代对象:在第0代空间满员后,会判断第一代是否超出预算,如果超出 ,会检查第1代和第0代对象,未被小队的第1代对象提升至第2代对象,第0代对象提升至第1代对象。

托管堆只支持3代:第0代、第1代、第2代。
CLR初始化时,会为每一代选择预算空间。CLR的垃圾回收器是自调节的。
如果GC,没有回收到足够内存,会执行一次完整回收,如果还是不够,便会抛出OutOfMemoryException异常。

垃圾回收的触发条件:
CLR在检测第0代超过预算时触发一次GC,这是最常见的GC触发条件。
其他条件:
1.显示调用System.GC的静态Collect方法
2.Windows报告低内存情况
3.CLR正在卸载AppDomain
4.CLR正在关闭

大小对象:
CLR将对象分为大对象和小对象,目前认为85000字节以上的对象是大对象。CLR以不同的方式对待大小对象:
1.大对象不是在小对象的地址空间分配,而是在进程地址空间的其他地方分配。
2.目前版本GC不压缩大对象。在进程中的大对象之间造成地址空间的碎片化,以至于抛出OutOfMemoryException。
3.大对象总是第二代,绝不可能是第0代或是第二代。

垃圾回收模式:
CLR启动时会选择一个GC模式,进程终止前该模式不会改变。
1.工作站

该模式针对客户端应用程序优化GC。GC造成的延时低,应用程序线程挂起时间短。该模式中,GC假定机器上运行的其他应用程序都不会消耗太多的CPU资源

2.服务器

该模式针对服务器端应用程序优化GC。被优化的主要是吞吐量和资源利用。GC假定机器上没有运行其他应用程序,假定机器的所有CPU都可用来辅助完成GC。该模式造成托管堆被拆分成几个区域,每个CPU一个。开始垃圾回收时,来及回收器在每个CPU上都运行一个特殊线程;每个线程都和其他线程并发回收它自己的区域。对于工作者线程行为一致的服务器应用程序,并发能很好的进行

两种子模式:
1.并发
2.非并发

强制垃圾回收:
System.GC类型允许应用程序对垃圾回收器进行一些直接控制。
抵用GC类的Collect 方法可以强制垃圾回收。

需要特殊清理的类型
包含本机资源的类型被GC时,GC会回收对象在托管堆中使用的内存,但会造成本机资源的泄漏,所以CLR提供了终结的机制,在对象被判定为垃圾后,在对象回收之前执行一些代码。任何包装了本机资源的类型都支持终结。
System.Object 定义了受保护的虚方法Finalize。垃圾回收器判定对象是垃圾后,会调用对象的Finalize方法。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘建宁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值