美文网首页
.Net 自动内存管理

.Net 自动内存管理

作者: 不正经的搬砖工 | 来源:发表于2021-09-17 14:13 被阅读0次

.Net依赖CLR(公共语言运行时)实现自动内存管理,标准CLR使用分代式标记-压缩GC对托管堆上对象进行自动内存管理。

1、GC过程

(1)标记阶段:从应用程序的根找到所有可达对象进行标记并创建一个对象引用图;

每个应用程序都有一组根,应用程序的根包含线程堆栈上的静态字段、局部变量和参数以及CPU寄存器。垃圾回收器可以访问由实时编译器(JIT)和运行时维护的活动根的列表。

(2)清除阶段:

a、未标记对象如果没有终结器(析构函数)则立即回收;

b、有终结器对象在创建时会将对象引用放到终结队列,当此对象变为垃圾时,垃圾收集器会将其引用从终结队列移到f-reachable队列。GC完成后一个终结器线程会遍历f-reachable队列,获取每个对象执行其Finalize方法,待该对象的Finalize方法执行完后,将该对象引用从f-reachable队列移除。这些对象会在下一次GC中回收(除非该对象复活);

如果某个终结器对象在终结器过程中复活(Resurrection),但是复活时不会将对象的引用重新放到终结队列,如果想让复活对象下次回收时执行Finalize方法,可以在对象复活时手动调用GC.ReRegisterForFinalize(obj)将该对象的引用添加到终结队列。

另外GC.SuppressFinalize(Object obj)可以将对象的引用从终结队列移除,垃圾回收时不再执行Finalize方法。一般SuppressFinalize用于同时实现了Finalize和Dispose方法来释放资源的情况下,在Dispose方法中调用GC.SuppressFinalize(this)。Finalize方法作为忘记调用Dispose释放资源的一个保障。

(3)压缩阶段:清除完后将所有存活对象移到堆的起始位置。压缩可以防止碎片化,同时避免耗时的空内存片段列表维护,直接用简单的策略将堆的尾部内存分配给新对象。

2、GC触发时机:

(1)由托管堆上已分配对象的使用内存超过特定阈值(该阈值可能随着进程的运行不断调整)时;

(2)调用System.GC.Collect手动触发;

(3)系统具有低的物理内存。通过OS的内存不足通知或主机指示的内存不足检测出来。

3、CLR中GC优化

3.1、分代回收

垃圾回收器将堆上内存对象分为3代:

第0代:新分配对象及从未经过垃圾回收的对象集合。通常仅有几百KB到几MB;

第1代:第0代回收中存活的对象集合;

第2代:第1代和第2代中未回收的对象集合。

因此可以单独处理长生存期和短生存期对象。每一代都维护一个阀值,当第n代内存达到该阀值时会触发对第n代的收集,当垃圾回收器检测到某个代中的幸存率很高时,会增加该代的分配阈值。回收一代时同时回收它前面的所有代。

图2-1 堆上的各代内存(引用C#7.0核心技术指南)

3.2 、大对象堆(Large Object Heap,LOH)

加载CLR时,GC分配两个初始堆段:一个用于小型对象(小对象堆SOH),一个用于大型对象(大对象堆 LOH)。垃圾回收器将大于等于一个阈值(目前是85000字节)的对象分配到大对象堆。大对象堆上对象都按第2代处理,可以避免过量的第0代回收。由于复制大型对象代价太大,默认不压缩大对象堆,所以需要维护空闲内存块链表,并会产生碎片化问题。在.Net Core和.Net Framework 4.5.1以上版本中,可以使用GCSettings.LargeObjectHeapCompactionMode 属性按需压缩大对象堆。

3.3、并发和后台回收

(1)并发垃圾回收

仅适用于工作站垃圾回收的.Net Framework 3.5及更早版本;服务器垃圾回收.Net Framework 4及更早版本。更高版本中,后台垃圾回收取代了并发垃圾回收。

并发垃圾回收只影响第2代垃圾回收,第0代和第1代的垃圾回收始终是非并发的。并发垃圾会输在一个专用线程上执行,运行并发垃圾回收线程的大多数时间,托管线程可以继续运行,最大程度减少因回收引起的暂停。

(2)后台垃圾回收

在.Net Framework 4 及更高版本中,后台垃圾回收替换并发垃圾回收。但在.Net Framework 4 中仅支持工作站垃圾回收,.Net Framework 4.5开始,后台垃圾回收可用于工作站和服务器垃圾回收。

后台垃圾回收只适用于第2代回收,后台垃圾回收在一个或多个专用线程上执行行,后台垃圾回收进行中,后台垃圾回收线程将在常见的安全点上检查,如果发现第0代或第1代空间不足需要前台垃圾回收时,后台垃圾回收会暂停自己并让前台垃圾回收(对暂时代(第0代和第1代)的回收)执行。前台垃圾回收完成之后,后台回收线程和用户线程将继续。

3.4、工作站和服务器垃圾回收

CLR提供以下类型的垃圾回收,可以基于工作负载的特征设置垃圾回收类型:

(1)工作站垃圾回收:为客户端应用设计;

回收发生在触发垃圾回收的用户线程上,并保留与用户线程相同的优先级(普通优先级),所以垃圾回收线程必须与其它线程竞争CPU时间。

(2)服务器垃圾回收:用于需要高吞吐量和可伸缩性的服务器应用程序

回收发生在以 THREAD_PRIORITY_HIGHEST 优先级运行的多个专用线程上,为每个CPU提供一个用于执行垃圾回收的一个堆和专用线程,多个垃圾回收线程一起工作。

3.4、垃圾回收通知(为担负大量请求的服务器应用准备)

服务器版本的CLR可以在完全垃圾回收之前发送通知。可以调用GC.RegisterForFullGCNotification启用通知。然后开启另一个线程持续监听GC.WaitForFullGCApproach(),当返回的GCNotificationStatus表示即将进行一次回收时,将工作负载重定向到另一个服务器实例,然后监听GC.WaitForFullGCComplete(),当该方法返回的状态表明回收完毕时在重新开始接受请求。

4、弱引用

CLR由System.WeakReference类实现弱引用,将Target属性设置为该对象。当垃圾收集器遇到一个弱引用指针指向对象时,不会将该对象加入引用关系图中。如果一个对象只有弱引用指向它,该对象不会被标记,垃圾回收时就可以清除此对象。

(1)短弱引用

垃圾回收回收对象后,弱引用的Target会变为null。弱引用本身时托管对象,也需要经过垃圾回收。

(2)长弱引用

在对象的Finalize方法调用后,长弱引用获得保留。这样便可以重新创建新对象。

若要建立强引用,可以将WeakReference的Target属性(前提是Target属性不为null,即对象未被回收)强制转换为对象类型。

相关文章

  • .Net 自动内存管理

    .Net依赖CLR(公共语言运行时)实现自动内存管理,标准CLR使用分代式标记-压缩GC对托管堆上对象进行自动内存...

  • C#:如何手工释放资源

    .NET 平台在内存管理方面提供了GC(Garbage Collection),负责自动释放托管资源和内存回收的工...

  • Java new一个Object对象占用多少内存?

    #refer: http://m.oschina.net/blog/208456 Java的自动内存管理机制(au...

  • jvm 基础第一节: jvm数据区

    程序内存管理分为手动内存管理和自动内存管理, 而java属于自动内存管理,因此jvm的职能之一就是程序内存管理 j...

  • 11-AutoreleasePool实现原理上

    我们都知道iOS的内存管理分为手动内存管理(MRC)和自动内存管理(ARC),但是不管是手动内存管理还是自动内存管...

  • About AutoReleasePool

    1、所有语言从内存管理上来说分两种,自动内存管理和非自动内存管理。自动内存管理不需要开发者回收内存,语言会带垃圾回...

  • 第021篇:内存管理与拷贝

    1、内存管理 1.1、内存管理基础(C语言)  内存分为栈区间和堆区间,栈区间的内存是系统自动申请自动释放;堆上的...

  • OC - OC的内存管理机制

    导读 一、为什么要进行内存管理 二、内存管理机制 三、内存管理原则 四、MRC手动内存管理 五、ARC自动内存管理...

  • iOS内存泄漏的场景和检测

    内存泄漏的相关定义OC当中内存管理方式:ARC/MRCARC:自动引用计数(系统自动管理内存),由开发人员开辟内存...

  • Java GC

    概述 GC => 垃圾回收 = 回收可用空间 + 压缩内存 内存管理 手动内存管理 => C | C++ 自动内存...

网友评论

      本文标题:.Net 自动内存管理

      本文链接:https://www.haomeiwen.com/subject/qwxfgltx.html