6

.NET GC 精要(一)

 3 years ago
source link: https://blog.csdn.net/tkokof1/article/details/104070739
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

.NET GC 精要(一)

tkokof1 2020-01-22 16:15:11 2018

本文讲述了 .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(互操作相关引用)

  • 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)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK