.NET GC 精要(一)

本文讲述了 .NET GC 的一些细节知识,内容大部分来自于书籍 Under the Hood of .NET Memory Management
(注:本文假设你了解 .NET 的基础知识,譬如值类型,引用类型等)

基础

稍有 .NET 基础的朋友一定知道 .NET GC 管理的是托管堆(managed heap)的内存释放问题,而托管堆又可以进一步分成两类:

  • Small Object Heap(简称 SOH), 即小对象堆,用以存储小于 85000 字节大小的对象
  • Large Object Heap (简称 LOH), 即大对象堆,用以存储大于等于 85000 字节大小的对象(注:这种说法并不准确,但是在基础部分我们可以暂时这么理解)

譬如我们定义了如下类型:

class MyClass
{
    string Test = "Hello world Wazzup!";
    byte[] data = new byte[86000];
}

可以看到上面定义的 MyClass 类型的 Test 成员是带有 19 个字符的字符串,应该存储于 SOH, 而其 data 成员是一个 86000 大小的字节数组,应该存储于 LOH, 当然 MyClass 实例本身只是存储了两个引用,应该存储于 SOH,所以 MyClass 的内存分布如下图所示:

在这里插入图片描述

.NET GC 的流程在原理上其实还是比较简单的: 首先从 GC roots(GC 根)处开始遍历对象间的引用关系,并对遍历到的对象进行标记,遍历完成之后,我们对没有标记的对象进行清理(即将其从 SOH 和 LOH 中去除),然后整个 GC 流程便完成了.

其中提到的所谓 GC roots(GC 根),包括以下几个部分(注:以下总结的并不全面,但是在基础部分我们可以暂时这么理解):

  • global/static object references(全局/静态对象引用)

  • CPU registers(CPU 寄存器)

  • object finalization references(对象终结器相关引用)

  • Interop references(互操作相关引用)

    扫描二维码关注公众号,回复: 8812054 查看本文章
  • stack references(栈引用)

而对于 GC 流程中最后的清理操作(将对象从 SOH 和 LOH 中去除),其实细节上还是比较复杂的,这次我们先简单讲讲 SOH:

正如之前所说, SOH 用以存储小对象,而小对象的申请与释放在一般的程序中是比较频繁的, 为了优化小对象的申请速度, SOH 是以内存连续的方式存储对象的,并且维护了一个称为 Next Object Pointer(NOP) 的引用,用以指示下一个可用的内存位置,通过 NOP,当遇到新的对象申请时, SOH 就可以快速的获取到可用的内存位置,这里贴个示意图:

在这里插入图片描述

SOH 的这种连续存储对象的方式虽然在申请对象时非常快速,但是在释放对象的时候却会遇到问题,考虑上面示意图中的对象A(Object A),因为没有被引用的关系,其会在 GC 流程中被清除,但是一旦其被清除,其所占用的内存对于 SOH 来说就不可用了(因为 SOH 通过 NOP 获取可用的内存位置),这就产生了内存碎片问题,为了解决这个问题, SOH 在清理的过程中会进行内存压缩(memory compaction),方法上就是将标记的对象移动到未标记的对象内存处(并处理一些内存空隙问题),用以保持 SOH 中内存的连续,仍然拿之前的示意图举例,经过 GC 清理及内存压缩处理之后, 上面的 SOH 大概如下图所示:

在这里插入图片描述

未完待续(to be continued)

发布了142 篇原创文章 · 获赞 146 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/tkokof1/article/details/104070739
GC