Clr Via C#读书笔记
#1 垃圾回收平台的基本工作原理:
访问一个资源所需的具体步骤:
1)调用IL指令newobj,为代表资源的类型分配内存。在C#中使用new操作符,编译器就会自动生成该指令。
2)初始化内存,设置资源的初始状态,使资源可用。类型的实例构造器负责设置该初始状态。
3)访问类型的成员(可根据需要反复)来使用资源。
4)摧毁资源的状态以进行清理。正确清理资源的代码要放在Finalize, Dispose和Close方法。
5)释放内存。垃圾回收器独自负责这一步。
托管堆如何知道应用程序不再用一个对象?
托管堆是CLR中自动内存管理的基础。初始化新进程时,运行时会为进程保留一个连续的地址空间区域。这个保留的地址空间被称为托管堆。托管堆维护着一个指针(NextObjPtr),用它指向将在堆中分配的下一个对象的地址。最初,该指针设置为指向托管堆的基址。
newobj指令将导致CLR执行以下步骤:
1) 计算类型(及其所有基类型)的字段所需要的字节数。
2) 加上对象的开销所需要的字节数。包括类型对象指针和同步索引块。32位程序需要增加8字节,64位程序需要增加16字节。
3) CLR检查保留区域是否能够提供分配对象所需要的字节数,如有必要就提交存储。如果托管堆有足够的可用空间,对象会被放入。
托管堆上连续分配的对象会由于引用的locality而获得性能上的提升,而且对象可以全部驻留在CPU缓存中,不会因为cache miss而被迫访问较慢的RAM.
托管堆之所有有这些好处,是因为它做了一个假设--地址空间和存储是无限的。托管堆通过垃圾回收器来允许它做这样的假设。
应用程序调用new操作符创建对象时,如果第0代堆满,执行一次垃圾回收。在一次垃圾回收中存活下来的对象被提升到另一代(例如第1代)。
#2 垃圾回收算法:
每个应用程序都包含一组根(root)。静态字段,方法参数和局部变量均被认为是一个根。只有引用类型的变量才被认为是根。值类型的变量永远都不被认为是根。此外,CPU寄存器也被视作根。
垃圾回收器开始执行时,假设堆中的所有对象都是垃圾,然后通过标记(对有跟引用的进行标记)和压缩(回收没有标记的对象)进行垃圾回收。
标记阶段:垃圾回收器沿着线程栈上行以检查所有根,如果发现一个跟(root)引用了一个对象(直接引用或间接引用),就在对象的"同步索引字段"上开启一位(将一个bit设置为1)进行标记。
压缩阶段:垃圾回收器线性的遍历堆,以寻找未标记对象的连续内存块。若果内存块较小,垃圾回收器会忽略该块。移动内存中的对象后,包含"指向这些对象的指针"的变量和CPU寄存器现在都会变得无效,垃圾回收器需要遍历修改所有根来指向新的内存位置。
#3 垃圾回收与调试:
class DebuggingRoots { public static void Go() { var t = new System.Threading.Timer(TimerCallback, null, 0, 2000); Console.ReadLine(); // 在ReadLine之后引用t,这种方式会被编译器优化掉 //t = null; // 在ReadLine之后引用t,防止其在Dispose方法返回之前被垃圾回收 //t.Dispose(); } private static void TimerCallback(Object o) { Console.WriteLine("In TimerCallback: " + DateTime.Now); // 出于演示目的强制执行垃圾回收 GC.Collect(); } }
#4 使用终结操作释放本地资源:
System.Threading.Mutex类型要打开一个Windows互斥体内核对象(本地资源)并保存其句柄,并在调用Mutex的方法时使用该句柄。
值类型(含所有枚举类型)、集合类型、String、Attribute、Delegate和Exception所代表的资源无需执行特殊的清理操作。如果一个类型代表着(或包装着)一个非托管资源(比如文件、数据库连接、套接字、mutex、位图、图标等),在对象的内存准备回收时,必须执行一些清理代码。实现了Finalize方法的任何类型实际上是在说,它的所有对象都希望"在被处决之前吃上最后一餐"。
1) 使用CriticalFinalizerObject类型确保终结
CLR赋予这个类以下三个功能:
温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/67708.html
- 上一篇:程序集的加载和反射
- 下一篇:【原创】浅说windows下的中断请求级IRQL