【CLR】GC垃圾回收

垃圾回收算法

  1. 引用计数法:堆上的每个对象都维护了一个内存字段用来记录有多少个对象引用了自己,当这个字段的值变成0时,就意味着自己没有用了,可以被回收走了。当下一个GC周期到来时,他就会被回收走。这种算法存在因循环引用导致无法回收的问题。
  2. 引用跟踪法:所有引用类型的变量都称作。然后查看此根都引用了哪些对象,然后看这个对象里又有哪些根,这些根又引用了哪些对象。以此类推,这么一来就可以构建一个个引用跟踪图,这些树上的对象的同步块索引的某一位就标记为1。

引用跟踪法回收流程

  1. 除了回收线程之外,暂停其它所有线程,防止在回收期间造成对象状态改变
  2. 标记阶段:遍历堆中所有对象,将这些对象同步块索引中的某一位全部标记为0,表示都应该被删除。然后CLR检查所有的活动根,看他们引用了哪些对象,并将引用的对象标记为1,然后再看这些对象的活动根引用了哪些对象,并也标记为1。标记结束之后,堆中的对象要么为0,要么为1。
  3. 压缩阶段:相当于碎片整理。压缩所有标记为1的对象,使它们占用连续的内存空间。压缩后对象在内存中的位置变了,所以还要再更新对象的指针。

代:提升性能

CLR的GC是基于代的GC,代的概念正式基于对代码的以下假设:

  1. 对象越新,活的越短
  2. 对象越老,活的越久
  3. 只回收部分对象快于回收所有对象

当然了,经过研究和实践,CLR的这些假设对于现今的程序来说都是成立的。
一共有几代?
托管堆只支持三代,第0代,第1代和第2代。每一代都有一个占用内存空间的预算,单位是KB。当然这个预算的大小不是固定的,假如在某次回收之后,第0代所剩的对象没有几个了,那么CLR就会减少第0代的预算,相关如果剩的对象还有很多,那么CLR就会增加预算。

代的工作原理

  1. 堆初始化时不包含任何对象,添加到堆的对象称为第0代对象。随着软件的运行,0代内对象越来越多,如果当再次给新对象分配内存时,会造成第0代超出预算,那么就会触发一次GC。
  2. 第0代内经过GC仍然活下来的对象,会称为第1代对象。此时第0代对象为空。
  3. 当软件继续运行,不断有新的对象添加第0代里,当添加某个对象又会造成第0代超出预算时,就又会触发GC。
  4. 此时GC会标记第1代和第0代,发现第1代里对象很少所以就不进行标记了(即使存在标记为0的对象),直接标记压缩第0代里的对象,并将活下来的对象标记为第1代。这么做正是基于对象越新活的越短的假设,可以快速的构建一个引用跟踪图。但是这么会存在一个问题,假如第1代的老对象新引用了0代的新对象,而GC不检查第1代的老对象,那第0代的新对象有可能会被错误的回收。不用担心JIT内部有一个机制,这个机制在对象的引用字段发生变化时会这只一个标志位。这样,垃圾回收器就知道自上次回收以来,哪些老对象已被更改,这些老对象会重新做检查。
  5. 随着软件的不断运行,第1代内的对象已经接近预算不能再多。此时第0代超出了预算,触发了一次GC。这次GC就会检查第1代和第0代内所有的对象,将第1代存活的对象标记为第2代,第0代存活的对象标记为第1代。
  6. 死死生生,来尔复往。。。

当GC没有回收到足够的内存时,就OutOfMemoryException了。32位进程大约有1.5G的可用内存,64位大约有8TB。

GC的触发条件

  1. 第0代超出预算时
  2. 调用GC.Collect()方法时
  3. Windows报告低内存时
  4. 卸载AppDomain时
  5. 关闭CLR时

大对象与小对象

目前所说的对象我们都是指小对象,一般认为超过85000byte的对象称为大对象,一般都是些json或者xml格式的字符串。CLR对待大对象与小对象的方式不同:

  1. 大对象内存是在进程的其它地址空间分配,与小对象不是一个地方
  2. GC不压缩大对象,因为在内存中移动它们代价太高,所以会有内存碎片的问题。但是保不准以后的CLR版本不会不压缩。
  3. 大对象总是在第2代。

GC对可终结(Finalize)对象的处理

当一个对象重写了基类的Finalize方法时,称为可终结对象。这种对象被回收时需要先调用它的Finalize方法。如果对象的类型定义了Finalize方法,那么在该类的构造函数调用之前,会把指向该对象的指针放到一个终结列表中,这个列表是GC控制的一个数据结构。

可终结对象的回收流程

  • 当终结列表中的某个对象被判定为垃圾时,会将这个对象的引用从终结列表中移除,然后添加到GC维护的另一个数据结构freachable队列中(发音:F-reachable,F代表Finalization)。
  • 这个对象因为还没调用Finalize方法,所以不可以被回收。其本身及引用的对象都会标记为1,代数+1。
  • 一个特殊的高优先级CLR终结线程专门调用Finalize方法,freachable不断出队列。
  • 当下一个GC到来时,发现终结列表和freachable列表都没有这个对象了,就会回收这个对象。

所以可终结对象的回收至少需要两次GC。

相关推荐
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页