从CLR GC到CoreCLR GC

前段时间在园子里看到有人提到了GC学习的重要性,很赞同他的观点。充分了解GC可以帮助我们更好的认识.NET的设计以及为何在云原生开发中.NET Core会占有更大的优势,这也是一个程序员成长到更高层次所需要经历的过程。在认识GC的过程中,我们先看一下.NET中内存分配的概要知识。
.NET分配内存,主要依据托管资源和非托管资源进行分配。托管资源分配到了托管堆中并受CLR的管理,非托管资源分配到了非托管堆中。该节主要讨论托管资源的分配。
CLR支持两种基本类型:值类型和引用类型。CLR对这两种类型在运行时有两种分配方式:

CLR内存操作

内存的分配过程如下图所示

memeryallot

需要注意的是,CLR还要维护一个指针,称为NextObjPtr,这个指针指向下一个对象再堆中的分配位置。初始化时,NextObjPtr设为地址空间区域的基地址。一个区域被非垃圾对象填满后,CLR会分配更多的区域,指针也会不断偏移。new操作符会返回对象的引用,就在返回这个引用之前,NextObjPtr指针的值会加上对象占用的字节数来得到一个新值,即下一个对象放入托管堆时的地址。

nextobjectptr

垃圾回收算法与GC运行机制

常用的垃圾回收算法主要有引用计数算法和引用跟踪算法。引用计算有着明显的缺陷,.NET使用的垃圾回收算法是引用跟踪法。小记:关于垃圾回收算法,我记得有一个知识点,在C#中如果出现了循环引用是否会导致内存溢出?如果比较了解这两种算法就会知道不会溢出。

GC Root

引用跟踪算法,通过一系列GCRoot对象作为起始点,从这些点开始向下搜索,搜索的路径成为引用链,当一个对象到GC没有任何引用链,说明对象可以被回收。

GC Root可以类比树来解释

gcroot

GC根节点存在于堆栈中,指向Teacher引用对象。它包含一个ArrayList订单集合,由Teacher对象引用。集合本身也包含对其元素的引用,随着搜索深度的增加,树也不断长大。

GC根节点的引用源来自

(1)、堆栈
(2)、全局或静态变量
(3)、CPU寄存器
(4)、互操作引用(COM / API调用中使用的.NET对象)
(5)、对象终结引用(objects finalization references)

GC运行机制

GC引入了代的概念,分为三种代,G0、G1、G2,G0对象生存周期较短,越往后生存周期越长(虽然G2中由于直接存储了大对象,又由于G2不是每次都会扫描,所以大多数情况下,G2中的对象的生存周期比G0中的更长)。

GC运行如下图所示

GC flow

需要注意的是,CLR想要进行垃圾回收时,会立即挂起执行托管代码中的所有线程,正在执行非托管代码的线程不会挂起。所以再多线程环境下,可能会出现莫名其妙的诡异问题。

下图为GC的整体运行流程,包含五个步骤:

gcflow

垃圾回收时机与模式

CLR会在一下情况发生时,执行GC操作

      1、当GC的代的预算大小已经达到阈值而无法对新对象分配空间的时候,比如GC的第0代已满;

  2、显式调用System.GC.Collect()(显示调用要慎重,因为手动调用可能会与自动执行的GC冲突,从而导致无法预知的问题);

  3、其他特殊情况,比如,操作系统内存不足、CLR卸载AppDomain、CLR关闭,甚至某些极端情况下系统参数设置改变也可能导致GC回收。

关于GC模式主要有

WorkStation GC

Server GC

Concurrent GC

Non-Concurrent GC

Background GC

详细信息请参阅:https://www.cnblogs.com/dacc123/p/10980718.html,这篇文章关于GC模式的说明比较详细。

.NET Core 3.0的GC处理

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwzjyy.html