//像背书一样,记录下吧

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寄存器等

垃圾回收的过程

  1. 标记阶段 : GC开始工作的时候,将所有对象都认为是可收集的,然后从应用程序的根出发,看它引用了哪些对象,就在它们的同步块索引上标记一位,表示它还不能回收,然后再看这些被根引用的对象又引用了哪些对象,再次递归标记...知道找出所有可达的对象,标记出他们...
  2. 压缩阶段 : 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的更多相关文章

  1. 不得不知的CLR中的GC

    引言 GC 作为CLR的垃圾回收器,让程序员可以把更多的关注度放在业务上而不是垃圾回收(内存回收)上.其实很多语言也有类似的东东, 如Java也有JIT 等等 GC基本概念 垃圾回收机制的算法有好多种 ...

  2. 什么是.NET CLI CLR IL JIT GC,它们是如何工作的

    参考网址: https://cloud.tencent.com/developer/article/1432891 1:什么是.NET? NET 是 Microsoft 的用以创建 XML Web 服 ...

  3. 一文了解.Net的CLR、GC内存管理

    一文了解.Net的CLR.GC内存管理 微软官方文档对内存管理和CLR的概述 什么是托管代码? 托管代码就是执行过程交由运行时管理的代码. 在这种情况下,相关的运行时称为公共语言运行时 (CLR),不 ...

  4. CLR和.Net对象生存周期

    标签:GC .Net C# CLR 前言 1. 基础概念明晰 * 1.1 公告语言运行时 * 1.2 托管模块 * 1.3 对象和类型 * 1.4 垃圾回收器 2. 垃圾回收模型 * 2.1 为什么需 ...

  5. 《代码的未来》读书笔记:内存管理与GC那点事儿

    一.内存是有限的 近年来,我们的电脑内存都有好几个GB,也许你的电脑是4G,他的电脑是8G,公司服务器内存是32G或者64G.但是,无论内存容量有多大,总归不是无限的.实际上,随着内存容量的增加,软件 ...

  6. CLR线程概览(一)

    托管 vs. 原生线程 托管代码在“托管线程”上执行,(托管线程)与操作系统提供的原生线程不同.原生线程是在物理机器上执行的原生代码序列:而托管线程则是在CLR虚拟机上执行的虚拟线程. 正如JIT解释 ...

  7. .NET CLR 运行原理

    原文: Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects 文章讨论了: SystemDoma ...

  8. 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配

    垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己 ...

  9. asp.net 之 GC (垃圾回收机制)

    今天抽时间好好整理了下GC相关知识,看了CSDN和博客园的几篇文章,有了一定的简单了解,决定根据个人理解整合一份随笔写下来,望诸位指教. 一:基础问题 1.首先需要知道了解什么是GC? GC如其名,就 ...

随机推荐

  1. 查看linux版本和内核信息

    一.查看Linux内核版本命令(两种方法): 1.cat /proc/version [root@localhost ~]# cat /proc/versionLinux version 2.6.32 ...

  2. 使用RDS不得不知的注意事项

    使用RDS不得不知的注意事项 1.RDS实例升级需要注意的事项 RDS在进行实例升级的过程中会出现最长30秒左右的连接闪断,需要您提前做好准备,并设置好程序跟RDS的自动重连,避免因为升级的闪断导致您 ...

  3. html与css的移动端与pc端需要注意的事项

    一个移动端与pc端之间最主要的也就是尺寸问题,苹果与安卓的机型尺寸大小相差甚多,一个尺寸都会影响用户的体验.那么我们来了解一下一些常用的解决方法. 一般在网页中都会在头部有一些这样的代码 <me ...

  4. sql 根据时间获取数据

    获取当月数据 MONTH(时间字段)=MONTH(GETDATE()) and year(时间字段)=year(GETDATE()) 计算两个时间差了多少分钟 DATEDIFF(mi,'7:00',c ...

  5. js 计算两个日期之间的周数

    //返回两个日期相差的周数 function WeeksBetw(date1, date2) { //这里的date1,date2都是Date对象 var d1 = new Date(date1); ...

  6. Opencv 函数

    1.cvLoadImage:将图像文件加载至内存: 2.cvNamedWindow:在屏幕上创建一个窗口: 3.cvShowImage:在一个已创建好的窗口中显示图像: 4.cvWaitKey:使程序 ...

  7. [C入门 - 游戏编程系列] 环境篇

    这一系列笔记的代码都是在Ubuntu 14.04下编码并测试的,原因无他,因为我笔记本电脑只装了一个Ubuntu系统,其中唯一使用的第三方库SDL也是开源并且跨平台的.所以即使你用的是Windows或 ...

  8. redhat 5下源码安装nginx服务

    首先确保机器中已安装 gcc c++,libtool等工具,保证可执行源码安装 A.为了确保能在 Nginx 中使用正则表达式进行更灵活的配置,安装之前需要确定系统是否安装有 PCRE(Perl Co ...

  9. 《Programming WPF》翻译 第8章 4.关键帧动画

    原文:<Programming WPF>翻译 第8章 4.关键帧动画 到目前为止,我们只看到简单的点到点的动画.我们使用了To和From属性或者By属性来设计动画--相对于当前的属性值.这 ...

  10. Linux下编译第三方库的问题

    因为各个Linux发行版之间的差异还是挺大的,有一些预安装在系统上的基本库是不一样的(不仅仅是版本,有一些是有和无的区别). 那么问题来了: 编译第三方库./configure的时候一般我们不会定制那 ...