艾伟_转载:.NET Discovery 系列之七--深入理解.NET垃圾收集机制(拾贝篇)

简介: 本系列文章导航.NET Discovery 系列之一--string从入门到精通(上).NET Discovery 系列之二--string从入门到精通(勘误版下).NET Discovery 系列之三--深入理解.

本系列文章导航

.NET Discovery 系列之一--string从入门到精通(上)

.NET Discovery 系列之二--string从入门到精通(勘误版下)

.NET Discovery 系列之三--深入理解.NET垃圾收集机制(上)

.NET Discovery 系列之四--深入理解.NET垃圾收集机制(下)

.Net Discovery 系列之五--Me JIT(上)

.NET Discovery 系列之六--Me JIT(下)

.NET Discovery 系列之七--深入理解.NET垃圾收集机制(拾贝篇)

  关于.Net垃圾收集器(Garbage Collection),Aicken已经在“.Net Discovery 系列”文章中有2篇的涉及,这一篇文章是对上2篇文章的补充,关于“.Net Discovery 系列”文章索引请见本文结尾。

  第一节.垃圾回收算法与完整收集(Full GC)

  垃圾收集器就是跟踪所有被引用到的对象,整理对象不再被引用的对象,回收相应的内存,它使用“标记与清除”算法,分两步回收对象:

  Step 1.Mark-Sweep :从应用程序的root出发,利用相互引用关系,遍历其在Heap上动态分配的所有对象,指明需要回收的对象,标记出那些存活的对象,予以标记。

  Step 2.Compact: 对内存中存活的对象进行移动,修改它们的指针,使之在内存中连续,这样空闲的内存也就连续了,即完成了内存释放工作,也解决了内存碎片问题,这个过程也可以成为指针的压缩。

  垃圾收集器一般将托管堆中的对象分为3代,这可以通过调用GC.MaxGeneration得知,对象按照存在时间长短进行分代,最短的分在第0代,最长的分在第2代,第2代中的对象往往是比较大的,第二代空间被称作Large Object Heap,对于2代对象的回收,与第0、1代回收方式相比最大的不同在于,没有了指针移动的压缩过程。

      

图1 对象的回收

  如上图所示,左边的区域为第一次GC时的结构,需要注意的是GC标记的是那些存活的对象,而不是需要回收的,所以第一次回收,对象B、D没有被标记,所以被回收了,之后GC移动了对象内存指针,使空间连续。

  接下来看中间的部分,第二次GC开始了,C对象没有被标记,所以被回收了,接下来A、D、F三个对象被压缩,形成连续的内存空间,并且形成了第1、2、3代区域。

  接下来看最右边的部分,D对象没有被标记,由于D对象处于第2代中,所以回收D对象后,GC没有启动压缩步骤,因为对于大对象的指针移动,资源耗费成本很高。

  对于第2代的GC称为Full GC,新分配的对象在第0代(0代空间最大长度通常为256K),按地址顺序分配,它们通常是一些局部变量;第1代(1代空间最大长度通常为2 MB)是经过0代垃圾收集后仍然驻留在内存中的对象,它们通常是一些如表单,按钮等对象;第2代是经历过几次垃圾收集后仍然驻留在内存中的对象,它们通常是一些应用程序对象。

  可见一次Full GC需要的资源是最多的,可能是几秒或十几秒。

  托管堆的内存分配以段(Segment)为单位,CLR启动时通常为GC Heap创建2个段,分别用来存储第0、1代对象和第2代对象,以下是通过Windbg工具查看到的GC Heap情况:

图2 WinDbg 查看GC Heap情况

  可以看出,GC堆被分成了两个段,三代,每代起始地址十进制差值为12。

  在理解方面需要注意的是,GC回收的是程序中的引用类型,值类型是保存在堆栈之中,当值类型对象出了作用域后会自动释放内存----即弹栈,不需要垃圾收集器管理。

  第二节.GC的工作模式

  GC的工作模式分3种,Workstation GC with Concurrent GC off、 Workstation GC with Concurrent GC on、Server GC ,在.Net 2.0以上版本可以通过修改Config文件来改变GC工作模式,例如启用Server GC:

 
 
< configuration >
< runtime >
< gcServer enabled ="true" />
</ runtime >
</ configuration >

 

  或者通过.Net配置工具,查看“我的电脑”节点属性可以方便的改变GC工作模式,如下图:

图3 GC工作模式

  Workstation GC without Concurrent: 用于单CPU的服务器,策略引擎会调节GC工作频率,使用挂起->查找与标记->压缩->恢复的流程进行GC工作。

  Workstation GC with Concurrent: Concurrent GC与Non Concurrent GC模式相比,有着更敏捷的反应速度,Winform应用程序和Windows services 服务程序默认采用这种模式,单CPU机器上只能使用workstation GC方式,默认为 Workstation GC with Concurrent。

  在这种模式下,第0、1代的收集仍然是要暂时挂起应用程序的,只有在收集第2代时,才会并行处理,这种并行收集是利用多CPU。

  对Full GC进行并行处理,具体原理是将Full GC过程切分成多个短暂子过程对线程进行冻结,在线程冻结时间之外,应用程序仍然可。

  以正常运行。这主要通过将0代空间设置的很大,使Full GC时,CLR仍然能够在0代中进行内存分配,如果Full GC时0代内存也已用尽,那么应用程序将被挂起,等待Full GC的完成。

  Server GC: 用于多CPU的服务器,这种GC模式有着很高的性能和效率。这种模式下,CLR为每个CPU创建一个专用的GC线程,每个CPU可以独立的为相应的heap执行GC操作,这些GC线程是以非并发的形式工作的,收集工作与线程正常工作不能同时进行,这就是说第0、1、2代的收集都会挂起应用线程。

  在.Net 4.0中,有一种新的垃圾收集机制,叫做后台收集。这种机制以concurrent GC为基础的,如上文所讲,Workstation GC with Concurrent模式中,在Full GC过程时,CLR仍然能够在0代中进行内存分配,如果Full GC时0代内存也已用尽,那么应用程序将被挂起,等待Full GC的完成。

  这个过程在后台收集机制中是这样工作的,在进行Full GC时可以同时进行第0、1代收集,并且后台收集是一个独立线程完成的,这个进程任务优先级低于第0、1代收集,如果在后台收集中需要对第0、1代收集,后台收集将会等待第0、1代收集完成后再进行工作,当然第0、1代收集是需要短暂挂起应用的。

  后台收集还会根据策略引擎的指示,动态调节第0、1代的容量,减少前台收集(第0、1代收集)次数。

  第三节 .Net 4.0中的垃圾收集器

  在.Net 3.5 SP1中,FrameWork中新增了如下方法,并且在4.0中进行了优化,GC.RegisterForFullGCNotification 、GC.WaitForFullGCApproach 、GC.WaitForFullGCComplete 、GC.CancelFullGCNotification,这几个方法都是针对Full GC(完整收集)的。

  1.GC.RegisterForFullGCNotification:这个方法将返回一个将要Full GC的信号通知,该方法有2个参数:

 
 
int maxGenerationThreshold
int largeObjectHeapThreshold

 

  这两个参数的含义是指的是第2代中存活的对象个数和大对象堆中对象个数,满足这两个参数后,便会引发通知,由此看来LOH也许并不是第2代,.Net GC也许也并不只是3代,

  这一点在.Net Discovery 系列之三--深入理解.Net垃圾收集机制(上)中已有描述。

  2.GC.CancelFullGCNotification:取消已经注册的垃圾收集通知

  这两个方法调用示例:

代码
 
  
// Variable for continual checking in the
// While loop in the WaitForFullGCProc method.
static bool checkForNotify = false ;

// Variable for suspending work
// (such servicing allocated server requests)
// after a notification is received and then
// resuming allocation after inducing a garbage collection.
static bool bAllocate = false ;

// Variable for ending the example.
static bool finalExit = false ;

// Collection for objects that
// simulate the server request workload.
static List < byte [] > load = new List < byte [] > ();


public static void Main( string [] args)
{
try
{
// Register for a notification.
GC.RegisterForFullGCNotification( 10 , 10 );
Console.WriteLine(
" Registered for GC notification. " );

checkForNotify
= true ;
bAllocate
= true ;

// Start a thread using WaitForFullGCProc.
Thread thWaitForFullGC = new Thread( new ThreadStart(WaitForFullGCProc));
thWaitForFullGC.Start();

// While the thread is checking for notifications in
// WaitForFullGCProc, create objects to simulate a server workload.
try
{
int lastCollCount = 0 ;
int newCollCount = 0 ;
while ( true )
{
if (bAllocate)
{
load.Add(
new byte [ 1000 ]);
newCollCount
= GC.CollectionCount( 2 );
if (newCollCount != lastCollCount)
{
// Show collection count when it increases:
Console.WriteLine( " Gen 2 collection count: {0} " , GC.CollectionCount( 2 ).ToString());
lastCollCount
= newCollCount;
}

// For ending the example (arbitrary).
if (newCollCount == 500 )
{
finalExit
= true ;
checkForNotify
= false ;
break ;
}
}
}

}
catch (OutOfMemoryException)
{
Console.WriteLine(
" Out of memory. " );
}
finalExit
= true ;
checkForNotify
= false ;
GC.CancelFullGCNotification();

}
catch (InvalidOperationException invalidOp)
{

Console.WriteLine(
" GC Notifications are not supported while concurrent GC is enabled.\n "
+ invalidOp.Message);
}
}

 

  3.GC.WaitForFullGCApproach:用来获得垃圾收集器是否将要启动完整垃圾收集的工作,该方法返回GCNotificationStatus枚举值,当枚举为Succeeded时,

  你应当做一些工作,例如阻止手动调用GC.Collect()方法,以免浪费资源。

  该方法应与GC.WaitForFullGCComplete()同时使用,以确定CLR执行了完整垃圾收集。

 
 
// 查看是否将启动完整收集
GCNotificationStatus s = GC.WaitForFullGCApproach();
if (s == GCNotificationStatus.Succeeded)
{
// do not GC.Collect()
}
else if (s == GCNotificationStatus.Canceled)
{
// GC.Collect()
}

 

  4.GC.WaitForFullGCComplete:

  用来获得垃圾收集器是否已经完成完整垃圾收集:

代码
 
  
GCNotificationStatus s = GC.WaitForFullGCApproach ();
s
= GC.WaitForFullGCComplete ();
if (s == GCNotificationStatus.Succeeded) // 已经完成了Full GC
{
// do not GC.Collect()
}
else if (s == GCNotificationStatus.Canceled)
{
// GC.Collect()
}

 

目录
相关文章
|
4月前
|
小程序 安全 JavaScript
.NET微信网页开发之通过UnionID机制解决多应用用户帐号统一问题
.NET微信网页开发之通过UnionID机制解决多应用用户帐号统一问题
.NET微信网页开发之通过UnionID机制解决多应用用户帐号统一问题
|
安全 JavaScript API
.NET微信网页开发之通过UnionID机制,解决用户在不同公众号,或在公众号、移动应用之间帐号统一问题
.NET微信网页开发之通过UnionID机制,解决用户在不同公众号,或在公众号、移动应用之间帐号统一问题
191 0
.NET微信网页开发之通过UnionID机制,解决用户在不同公众号,或在公众号、移动应用之间帐号统一问题
|
存储 开发框架 Java
【CLR C#】浅谈.Net的GC(垃圾回收)机制及其整体流程
在.NET程序开发中,为了将开发人员从繁琐的内存管理中解脱出来,将更多的精力花费在业务逻辑上,CLR提供了自动执行垃圾回收的机制来进行内存管理,开发人员甚至感觉不到这一过程的存在。.NET程序可以找出某个时间点上哪些已分配的内存空间没有被程序使用,并自动释放它们。自动找出并释放不再使用的内存空间机制,就称为垃圾回收机制。本文主要介绍.Net中的GC(垃圾回收)机制及其整体流程。
【CLR C#】浅谈.Net的GC(垃圾回收)机制及其整体流程
|
缓存 运维 安全
『容错机制』 .NET 弹性和瞬态故障处理库Polly应用详解
所谓瞬态故障,就是说故障不是必然会发生的,而是偶然可能会发生的,比如网络偶尔会突然出现不稳定或无法访问这种故障。至于弹性,就是指应对故障 Polly 的处理策略具有多样性和灵活性,它的各种策略可以灵活地定义和组合。
368 1
『容错机制』 .NET 弹性和瞬态故障处理库Polly应用详解
|
开发框架 监控 .NET
ASP.NET Core : 二十三. 深入聊一聊配置的内部处理机制(四)
上一章介绍了配置的多种数据源被注册、加载和获取的过程,本节看一下这个过程系统是如何实现的。
96 0
ASP.NET Core : 二十三. 深入聊一聊配置的内部处理机制(四)
|
XML 存储 JSON
ASP.NET Core : 二十三. 深入聊一聊配置的内部处理机制(三)
上一章介绍了配置的多种数据源被注册、加载和获取的过程,本节看一下这个过程系统是如何实现的。
116 0
ASP.NET Core : 二十三. 深入聊一聊配置的内部处理机制(三)
|
JSON 开发框架 .NET
ASP.NET Core : 二十三. 深入聊一聊配置的内部处理机制(二)
上一章介绍了配置的多种数据源被注册、加载和获取的过程,本节看一下这个过程系统是如何实现的。
92 0
ASP.NET Core : 二十三. 深入聊一聊配置的内部处理机制(二)
|
JSON 开发框架 .NET
ASP.NET Core : 二十三. 深入聊一聊配置的内部处理机制(一)
上一章介绍了配置的多种数据源被注册、加载和获取的过程,本节看一下这个过程系统是如何实现的。
159 0
|
JSON 开发框架 .NET
ASP.NET Core: 二十. Action的多种数据返回格式处理机制(四)
上一章讲了系统如何将客户端提交的请求数据格式化处理成我们想要的格式并绑定到对应的参数,本章讲一下它的“逆过程”,如何将请求结果按照客户端想要的格式返回去。
412 0
|
开发框架 JSON .NET
ASP.NET Core: 二十. Action的多种数据返回格式处理机制(三)
上一章讲了系统如何将客户端提交的请求数据格式化处理成我们想要的格式并绑定到对应的参数,本章讲一下它的“逆过程”,如何将请求结果按照客户端想要的格式返回去。
230 0
ASP.NET Core: 二十. Action的多种数据返回格式处理机制(三)