垃圾回收

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了垃圾回收相关的知识,希望对你有一定的参考价值。

CLR管理的内存块

  • 线程的堆栈
  • GC堆
  • LOH(large Object Heap)堆对象大于85000字节

托管堆=GC Heap + Loader Heap

什么样的对象会被回收

GC只回收GC堆中的对象,堆栈的不管
只有当前不可达的对象会被回收

先介绍下应用程序根这个概念,应用程序根可以是:

  • 本地变量或者全局变量(C#不支持全局变量,但CIL允许)
  • 类型的静态成员或者静态属性
  • 传递到方法中的参数变量
  • CPU寄存器所引用的对象

技术分享

如图中在运行RunTest时, _itemitem都是应用程序根,但如果RunTest执行完,item则被栈销毁,Phone对象少了一个能引用他的根

但Phone不会被销毁,因为静态变量_item始终引用着它。
相反,图中对象3与对象4,没有根引用它们,就当前运行环境而言无法获取它们,所以它们就是下一次垃圾回收的对象

怎么回收

就算法而言,GC采用的是mark-and-compact(标记和压缩)
先假设所有对象是可回收,再根据找到可达对象,把他添加到可达对象图(如果可达对象引用了其他对象,那“其他对象”也是可达的),最后将不可达的对象清空,留下的对象重新排布,压缩内存.
技术分享
在压缩内存时,GC会重新找到一个新的较大的连续空间,将对象复制过来,并修改所有引用

显然这种操作是耗性能的,有时候就是会遇到生命周期比较长的对象,如果频繁地对它垃圾回收,势必会产生很多压缩内存的消耗。

于是GC结合的机制提升垃圾回收性能
这种机制的对你的代码做出以下几点假设

  • 对象越新,生命周期越短
  • 对象越老,生命周期越长
  • 回收堆的一小部分,速度快与回收整个堆

该机制将对象分成三代,分别是0,1,2代。每一代的大小依次增大,0代最小,2代最大。
代的大小在运行时也可能会调整,并不是固定的。
新的对象都视为0代,垃圾回收时存活下来的0代会被提升到1代,当0代与1代空间都不足时,1代的对象才会提升到2代(同时0代提升到1代)

技术分享

什么时候回收

  • 调用GC.Colect
  • 内存不足(0代满了)
  • CLR卸载AppDomain
  • CLR关闭

大对象

对象大于85000字节时会保存到LOH(large Object Heap)堆

  • 没有代级的概念,所有对象都被视为第2代。
  • 不进行对象移动和空间压缩,因为移动大对象是相对耗时的操作。因此,需要一个链
    表来维护空闲区域的位置。
  • 对象不会被分配在末尾,而会在链表中寻找合适的位置,因此会存在碎片的问题

资料

  • 《CLR via in C#》
  • 《.Net之美》
  • 《你必须知道的.Net》









以上是关于垃圾回收的主要内容,如果未能解决你的问题,请参考以下文章

JVM垃圾回收器之G1(Garbage First)--new

java中垃圾回收机制的原理

JavaScript的垃圾回收机制

Java内存与垃圾回收篇(对象内存与垃圾回收机制)上篇

浅析C#中的托管非托管堆栈与垃圾回收

浅析C#中的托管非托管堆栈与垃圾回收