.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()
}
转自:http://www.cnblogs.com/isline/archive/2010/01/01/1637241.html
.Net Discovery 系列之七--深入理解.Net垃圾收集机制(拾贝篇)的更多相关文章
- .Net Discovery系列之三 深入理解.Net垃圾收集机制(上)
前言: 组成.Net平台一个很重要的部分----垃圾收集器(Garbage Collection),今天我们就来讲讲它.想想看没有GC,.Net还能称之为一个平台吗?各种语言虽然都被编译成MSIL,但 ...
- .Net Discovery系列之四 深入理解.Net垃圾收集机制(下)
上一节给大家介绍了 .Net GC的运行机制,下面来讲下与GC相关的重要方法. 第二节.GC关键方法解析 1.Dispose()方法 Dispose可用于释放所有资源,包括托管的和非托管的,需要自己实 ...
- .Net Discovery 系列之五--深入浅出.Net实时编译机制(上)
欢迎阅读“.Net Discovery 系列”文章,本文将分上.下两部分为大家讲解.Net JIT方面的知识,敬请雅正. JIT(Just In Time简称JIT)是.Net边运行边编译的一种机制, ...
- 深入理解JVM垃圾收集机制,下次面试你准备好了吗
程序计数器.虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失,因此不需要对这三个区域进行垃圾回收.垃圾回收主要是针对 Java 堆和方法区进行. 判断一个对 ...
- 深入理解JVM垃圾收集机制(JDK1.8)
垃圾收集算法 标记-清除算法 最基础的收集算法是"标记-清除"(Mark-Sweep)算法,分两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象. 不足: ...
- .Net Discovery 系列之六--深入浅出.Net实时编译机制(下)
接上文 在初始化时,HashTable中各个方法指向的并不是对应的内存入口地址,而是一个JIT预编译代理,这个函数负责将方法编译为本地代码.注意,这里JIT还没有进行编译,只是建立了方法表! 下表(表 ...
- .Net Discovery系列之十二-深入理解平台机制与性能影响(下)
上一篇文章中Aicken为大家介绍了.Net平台的垃圾回收机制.即时编译机制与其对性能的影响,这一篇中将继续为大家介绍.Net平台的异常捕获机制与字符串驻留机制. 三.关于异常捕获机制 虽然我们已经很 ...
- .Net Discovery系列之十-深入理解平台机制与性能影响(上)
转眼间<.Net Discovery>系列文章已经推出1年了,本文为该系列的第10-13篇文章,在本文中将对以前所讲的.Net平台知识做一个小小的总结与机制分析,引出并重点介绍这些机制对程 ...
- Java 虚拟机系列二:垃圾收集机制详解,动图帮你理解
前言 上篇文章已经给大家介绍了 JVM 的架构和运行时数据区 (内存区域),本篇文章将给大家介绍 JVM 的重点内容--垃圾收集.众所周知,相比 C / C++ 等语言,Java 可以省去手动管理内存 ...
随机推荐
- scrapy主动触发关闭爬虫
在spider中时在方法里直接写 self.crawler.engine.close_spider(self, 'cookie失效关闭爬虫') 在pipeline和downloaderMiddle ...
- MVC 控制器中传递dynamic(对象) 给视图
有时候不想重新定义一个实体,则使用 dynamic 来定义匿名类型. //匿名类型 传递到前台 Model dynamic viewModel = new { UserID = 5016 }; ret ...
- SharePoint 2013 另一个程序正在使用此文件,进程无法访问。 (异常来自 HRESULT:0x80070020)
环境:SharePoint 2013 + Windows Server 2012 R2 在管理中心新建一个Web Application,端口为:88.顺利创建网站集后,打开访问却提示:无法显示此页 ...
- Spring:@Cacheable 中condition条件的理解
condition=false时,不读取缓存,直接执行方法体,并返回结果,同时返回结果也不放入缓存. ndition=true时,读取缓存,有缓存则直接返回.无则执行方法体,同时返回结果放入缓存(如果 ...
- Vue中发送ajax请求——axios使用详解
axios 基于 Promise 的 HTTP 请求客户端,可同时在浏览器和 node.js 中使用 功能特性 在浏览器中发送 XMLHttpRequests 请求 在 node.js 中发送 htt ...
- django-xadmin后台开发
先通过pip命令行安装django<=1.9版本 示例:pip install django==1.9 从https://github.com/sshwsfc/xadmin下载xadmin源码解 ...
- 从 prototype.js 深入学习 javascript 的面向对象特性
从 prototype.js 深入学习 javascript 的面向对象特性 js是一门很强大的语言,灵活,方便. 目前我接触到的语言当中,从语法角度上讲,只有 Ruby 比它更爽. 不过我接触的动态 ...
- 【LOJ】#2178. 「BJOI2017」机动训练
题解 遇见平方和就转有序对呗 dp类似从很多点出发每次走一步的转移方式 然后我too naive的,枚举路径长度来决定更新次数,愉快TLE 改成记搜就过了 代码 #include <bits/s ...
- Air Raid HDU 1151
题意 给定n个路口 加上一些单向路 求遍历完所有路口需要多少人 人是空降的 哪里都可以作为起点 求 最小边覆盖 (用最少的边覆盖所有的点) 也叫最小路径覆盖(更形象) 也叫二分图的最大独立集 ...
- java学习之借书系统
实现的图书借阅系统要处理用户输入的非法参数,并引导用户正确使用 测试结果: 主要目的就是练习异常处理中的Exception类的使用 使用的相关语法 try{ //可能产生异常的代码块 }catch(E ...