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

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. C#遍历Object各个属性含List泛型嵌套。

    同事遇到一个问题:在做手机app接口时,返回JSON格式,json里面的数据属性均是string类型,但不能出现NULL(手机端那边说处理很麻烦,哎).Model已经创建好了,而且model的每个属性 ...

  2. 如何禁用不需要的HTTP方法

    IIS7.0默认开启了不安全的OPTIONS和TRACE方法,建议关闭这两个方法. 以下环境为windows server 2008.IIS7.0 方法(1):web.config 在<conf ...

  3. IE6 浏览器常见兼容问题 大汇总(23个)

    IE6以及各个浏览器常见兼容问题 大汇总 综述:虽然说IE6在2014年4月将被停止支持,但是不得不说的是,IE6的市场并不会随着支持的停止而立刻消散下去,对于WEB前端开发工程师来说,兼容IE6 兼 ...

  4. PL/SQL语句块提高1+case语句

    set serveroutput on; declare --默认值的bianliang v_a ; -- v_b integer; --用stud.id 的类型 v_id stud.id%type; ...

  5. OC基础 单例

    #undef  AS_SINGLETON   #define AS_SINGLETON( __class ) \       + (__class *)sharedInstance;      #un ...

  6. 利用java Base64 实现加密、解密

    Base64加密解密 package com.stone.util; import java.io.UnsupportedEncodingException; import sun.misc.*; p ...

  7. [译]终极塔防——运用HTML5从头创建一个塔防游戏

    翻译共享一篇CodeProject的高星力作,原文地址:http://www.codeproject.com/Articles/737238/Ultimate-Tower-Defense 下载演示项目 ...

  8. 阅读书目_2014H1

    1.<程序员修炼之道 专业程序员必知的33个技巧>(完成) 注:更多是面向程序员全工作流程的. 2.linux shell脚本攻略 适合初学,但不方便作为参考手册查阅. 3.编写可读代码的 ...

  9. 完美解决ListView 与 ScrollView 共存问题

    1:首先设置ListView的高度,在setAdapter之后调用此方法. public static void setListViewHeightBasedOnChildren(ListView l ...

  10. poj2975--Nim

    题意:对于一个给定的取石子游戏,有多少种先手策略获胜? Ans:若无法获胜,则输出0. 若能获胜我们只要找到一堆石子,使得我们能取它的一部分让总和的异或和变为0.我们先将整个游戏的值异或起来为s 则a ...