CLR via C# - GC
//像背书一样,记录下吧
1.CLR分配资源
托管堆上维护着一个指针NextObjPtr.该指针表示下一个新建对象在托管堆上的位置.C#的new Object会产生IL newobj指令,NextObjPtr传递给this,构造完会返回对象的地址.
在托管堆中 连续分配的对象可以保证它们的地址也是连续的,可以得到性能方面的一些提升.
在托管堆上分配的对象,都有两个IntPtr的开销字段,一个是对象类型指针,二是同步块索引.在32位Application中都是4Byte=32bit,在64位Application上是64bit=
2.GC回收算法 & 回收过程 & 对象的代
垃圾收集器通过检查在托管堆中是否有应用程序不再使用的对象来回收内存.垃圾收集器是如何知道一个对象是否是不再使用的呢?
*其实回收的不是我们不再使用的对象,而是那些在当前作用域不能访问的对象.
应用程序的根:一个根即一个存储位置,包含着指向引用类型的对象指针,->托管堆中的对象 or null(空值).其实就是那些在当前代码/环境/context中可以访问的对象引用.如static 字段,局部变量,方法参数,CPU寄存器等
垃圾回收的过程
- 标记阶段 : GC开始工作的时候,将所有对象都认为是可收集的,然后从应用程序的根出发,看它引用了哪些对象,就在它们的同步块索引上标记一位,表示它还不能回收,然后再看这些被根引用的对象又引用了哪些对象,再次递归标记...知道找出所有可达的对象,标记出他们...
- 压缩阶段 : GC现在已将找出了所有可达的对象,GC会寻找连续的位被标记的内存区域(即会被回收的区域),如果找到较大的连续空间,GC会将 非垃圾对象 搬到这片连续的空间上,以压缩托管堆,如果碰到较小的内存块,GC会忽略不计.
那么GC怎么知道什么时候开始工作呢,来看对象的代
对象的代(generation)
托管堆中开始是空的,新添加的对象,被称为第0代,当应用程序占用内存很小时,没有必要去动用GC垃圾回收,况且这个垃圾回收可能使性能变差,但这个很小是什么概念,就是我们对分配对象占用的内存要有一个阀值,小于这个值,我们不需要GC工作,例如选择第0代对象256KB的预设空间,当分配的内存超出这个预设空间时,就会触发GC来回收内存.
经过第一次垃圾回收,有些对象没了,剩余存活的对象提升一个代,成为第一代,我们也为GC的第一代对象选择一个阀值,例如2MB.这时还在不断分配新对象,他们是第0代,当第0代的256KB满了之后,回收它,将存活的并入第1代...
经过不断回收,第1代的对象满了之后,GC会回收第1代,存活的提升为第2代,CLR via C#书上说第二代取10MB,第二代满了之后才会对第二代进行垃圾回收...
这样利用代的概念,减少GC回收要扫描的内存空间,减少GC对第1代,第二代扫描的频率.
3. 变量的存活期,以及调试器的干预
变量在不可达时就会在下一次垃圾回收时被回收.
来看个例子
public static void Main()
{
var timer = new System.Threading.Timer(delegate {
Console.WriteLine("timer elapsed at : " + DateTime.Now);
GC.Collect();
},null,,); System.Console.ReadLine();
}
这段代码,在VS 中Debug配置下正常工作(即按照预期,回调一直执行),在Realse配置下只会打印一行,即GC.Collect()将timer回收了
在Debug中为什么就不被回收呢?
是VS生成的程序集方便调试,将所有变量的存活期撑到方法结束,VS给程序集打上System.Diagnostics.DebuggableAttribute表示是在调试,并且在参数上指定禁用JIT优化代码
但是我们发布的时候可不能靠这个,我们得发布Realse版本,代码会被JIT优化,在上面的代码中,要保证对timer的引用,就不会被GC光顾~
修改代码
public static void Main()
{
var timer = new System.Threading.Timer(delegate {
Console.WriteLine("timer elapsed at : " + DateTime.Now);
GC.Collect();
},null,,); System.Console.ReadLine();
timer.Dispose();//保持对timer的引用
}
即可
4. Dispose Finalize函数,以及CriticalFinalizerObject
Dispose函数,手动调用,释放资源.
Finalize函数,在C#代码中,类的析构函数会被编译成终结函数(Finalize),供GC在垃圾回收该对象时自动调用
CriticalFinalizerObject类,在System.Runtime.ConstrainedExecution.CriticalFinalizerObject下面,该类没有任何实现,继承该类之后什么都不用做,会得到CLR and GC的特殊照顾.我们知道CLR在执行一个方法时JIT会将IL code编译成Native code,是到第一次执行时才编译,特么像解释型语言,但是CriticalFinalizerObject类型(包括派生类型)的Finalize方法会被立即编译,确保Finalize会被执行,本地资源会被释放.
同时在Finalize执行顺序上也有规则.CLR会先执行非CriticalFinalizerObject类的终结方法,后执行CriticalFinalizerObject类型的终结方法,因为这个特殊类型封装的本地资源最后调用析构函数,可以保证之前的终结方法调用时本地资源不被关闭
释放模式
在Winform项目里新建一个Form,打开Designer.cs文件,可以见到
Dispose()
{
Dispose(true);
}
void Dispose(bool disposing)
{
if(disposing)
{
//blabla
}
}
见到这种Code,即是释放模式:我们释放资源有两种情况,1是显式调用Dispose方法,2是GC在判定对象是要被垃圾回收时,调用终结函数
在IDisposable.Dispose和终结函数的实现中,我们均可以用Dispose(bool disposing)来实现
区别在于disposing参数,它表示是否是在显式调用Dispose()来释放资源,如果是,我们在本类中所引用的其他对象,可以确定他们可以访问,也就是还没被GC回收,可以在if(disposing)里回收这些对象,而GC在回收的时候,我们不确定在本类中所引用的对象是否已经被GC回收.公共部分可以释放一些GC无法处理的资源,如本地资源
5. FCL类库中的依赖,例 文件FIleStream 与 StreamWriter 依赖问题
var fs=new FileStream(path)
var sw=new StreamWriter(fs); sw.Write(xxx)
StreamWriter在Write的时候,会写到自己的缓冲区,在缓冲区满的时候,或者Close的时候,写到FileStream,同理FileStream缓冲区满了之后写到文件.
MS没有给StreamWriter实现终结方法,如果数据存在缓冲区,而且没有close来关闭StreamWriter以Flush数据的话,数据就会丢失.
在关闭StreamWriter的时候,会关闭相关联的FileStream,而不用显式关闭
CLR via C# - GC的更多相关文章
- 不得不知的CLR中的GC
引言 GC 作为CLR的垃圾回收器,让程序员可以把更多的关注度放在业务上而不是垃圾回收(内存回收)上.其实很多语言也有类似的东东, 如Java也有JIT 等等 GC基本概念 垃圾回收机制的算法有好多种 ...
- 什么是.NET CLI CLR IL JIT GC,它们是如何工作的
参考网址: https://cloud.tencent.com/developer/article/1432891 1:什么是.NET? NET 是 Microsoft 的用以创建 XML Web 服 ...
- 一文了解.Net的CLR、GC内存管理
一文了解.Net的CLR.GC内存管理 微软官方文档对内存管理和CLR的概述 什么是托管代码? 托管代码就是执行过程交由运行时管理的代码. 在这种情况下,相关的运行时称为公共语言运行时 (CLR),不 ...
- CLR和.Net对象生存周期
标签:GC .Net C# CLR 前言 1. 基础概念明晰 * 1.1 公告语言运行时 * 1.2 托管模块 * 1.3 对象和类型 * 1.4 垃圾回收器 2. 垃圾回收模型 * 2.1 为什么需 ...
- 《代码的未来》读书笔记:内存管理与GC那点事儿
一.内存是有限的 近年来,我们的电脑内存都有好几个GB,也许你的电脑是4G,他的电脑是8G,公司服务器内存是32G或者64G.但是,无论内存容量有多大,总归不是无限的.实际上,随着内存容量的增加,软件 ...
- CLR线程概览(一)
托管 vs. 原生线程 托管代码在“托管线程”上执行,(托管线程)与操作系统提供的原生线程不同.原生线程是在物理机器上执行的原生代码序列:而托管线程则是在CLR虚拟机上执行的虚拟线程. 正如JIT解释 ...
- .NET CLR 运行原理
原文: Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects 文章讨论了: SystemDoma ...
- 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配
垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己 ...
- asp.net 之 GC (垃圾回收机制)
今天抽时间好好整理了下GC相关知识,看了CSDN和博客园的几篇文章,有了一定的简单了解,决定根据个人理解整合一份随笔写下来,望诸位指教. 一:基础问题 1.首先需要知道了解什么是GC? GC如其名,就 ...
随机推荐
- 关于局域网内IIS部署网站,本机可访问,而网内其他用户无法访问问题的解决方法
在Window7操作系统中安装配置好IIS后,在本地IIS上部署网站程序没有问题,但是局域网等远程用户不能正常访问网站程序,提示“Internet Explorer 无法显示该网页”. 问题解决思路如 ...
- C++中的虚函数
代码: #include <iostream> #include <cstring> using namespace std; class Base{ public: virt ...
- PCL编译历程
boost 编译安装包下载地址: http://boost.teeks99.com/ boost安装:http://blog.sina.com.cn/s/blog_7c48b0f10102v0zj.h ...
- 搭建lamp环境
虚拟机始终是虚拟机,还是linux用起来舒服得多.话不多说,回到我们的老本行,linux下进行lamp环境搭建吧. 一.安装 1.Apache sudo apt-get install apache2 ...
- Unsupported major.minor version 52.0 处理方式
Exception in thread "main" java.lang.UnsupportedClassVersionError: com/globalroam/openstac ...
- :before与:after伪类的应用
1.小三角样式
- inline-block样式间距
原始问题和解决方法请参考 淘宝UED官方博客:inline-block 前世今生 布局时采用行内块display:inline-block,发现元素之间有空隙,原因是由于空白字符引起的,详细见上面链接 ...
- 一个很简单的jQuery插件实例教程(菜鸟级)
很多公司的前端设计开发人员都是女孩子,而这些女孩子很多JavaScript技能都不是很好.而前端开发过程中,JavaScript技能又是必不可少的.所以,如果前端小MM正在为某个JavaScript效 ...
- 我和小美的撸码日记--基于MVC+Jqgrid的.Net快速开发框架
前言:以前的帐号没有发首页的权限,特此把这篇文章从另外一个博客移过来,这篇是<我和小美的撸码日记>的序 一转眼务农6年了,呆过大公司也去过小作坊,码农的人生除了抠腚还是抠腚.在所有呆过的公 ...
- Keil C51内存分配与优化
C51的内存分配不同于一般的PC,内存空间有限,采用覆盖和共享技术.在Keil编译器中,经过编译后,会形成一个M51文件,在其内部可以详细的看到内存的分配情况. C51内存常见的两个误区: A.变量超 ...