您的位置:首页 > 编程语言 > C#

《CLR via C#》读书笔记 之 自动内存管理(垃圾回收)

2013-03-19 14:50 302 查看

第二十一章 自动内存回收(垃圾回收)

2013-03-19

本章讨论托管应用程序如何构造新对象,托管堆如何控制这些对象的生存期,以及如何回收这些对象的内存。

21.1 理解垃圾回收平台的基本工作原理
从托管堆分配资源
托管堆和C运行时堆比较
21.2 垃圾回收算法
21.3 垃圾回收与调试
21.4 使用终结器操作来释放本地资源
21.4.1使用CriticalFinalizerObject类型确保终结
21.6 什么会导致Finalize被调用
21.7 终结操作揭秘
21.8 Dispose模式:强制对象清理资源
21.10 C#using语句

21.14 代

21.1 理解垃圾回收平台的基本工作原理

返回

每个程序都要使用资源,比如文件、内存缓冲、网络连接、数据库资源等。事实上,在面向对象的环境里,每个类型都代表可供程序使用的一种资源。要使用这些资源,必须为代表资源的类型分配内存。以下是访问资源所需的具体步骤:

(1)调用IL指令newobj,为代表资源的类型分配内存。在C#中是new操作符;

(2)初始化内存,设置资源的初始状态,是资源可用。由类型的实例构造器负责;

(3)访问类型的成员来使用资源;

(4)摧毁资源状态来进行清理。(21.8 Dispose模式:强制对象清理资源);

(5)释放内存。由垃圾回收器负责。

在进行非托管编程时,常发生两种bug:程序员忘记释放不再需要的内存造成(内存泄露);试图调用已被释放的内存造成(对象损坏)。内存泄露、内存溢出以及解决方法

垃圾回收器(garbage collection)就是为了处理以上问题二产生的。

注意:(参考第四章 的 托管堆的内存分配机制)托管堆又根据存储信息的不同划分为多个区域,其中最重要的是垃圾回收堆(GC Heap)和加载堆(Loader Heap),GC Heap用于存储对象实例,受GC管理;Loader Heap又分为High-Frequency Heap、Low-Frequency Heap和Stub Heap,不同的堆上又存储不同的信息。Loader Heap最重要的信息就是元数据相关的信息,也就是Type对象,每个Type在Loader Heap上体现为一个Method Table(方法表),而Method Table中则记录了存储的元数据信息,例如基类型、静态字段、实现的接口、所有的方法等等。Loader Heap不受GC控制,其生命周期为从创建到AppDomain卸载。

从托管堆分配资源

IL指令newobj用于创建一个对象时,就会从托管堆调拨可用空间,步骤如下:

(1)计算类型(及所有基类型)的字段需要的字节数;

(2)加上对象两个附加字段:一个是类型对象指针,一个是同步索引块。

(3)CLR检查托管堆是否有足够的可用空间。若有,放入。注意:是在NextObjPtr指针后放入的,并且为它分配的字节清零。接着,调用类型的实例构造器(为this参数传递NextObjPtr)。而后,NextObjPtr的指针的值会加上对象占据的字节数,得到一个新值,如下图所示:

View Code

public static class Program
{
public static void Main()
{
byte[] bytesToWrite = new byte[] { 1, 2, 3, 4, 5 };
const string fileName = "Temp.dat";
//put dispose in finally block
OperateFile(bytesToWrite, fileName);
File.Delete("Temp.dat");

//use 'using' sentence instead
OperateFileinUsing(bytesToWrite, fileName);
File.Delete("Temp.dat");
}
private static void OperateFile(byte[] fileContent,string fileName)
{
FileStream fs = new FileStream("Temp.dat", FileMode.Create);
try
{
fs.Write(fileContent, 0, fileContent.Length);
}
finally
{
if (fs != null) fs.Dispose();
}
}
private static void OperateFileinUsing(byte[] fileContent, string fileName)
{
using(FileStream fs = new FileStream("Temp.dat", FileMode.Create))
{
fs.Write(fileContent, 0, fileContent.Length);
}
}
}


注意:using语句只能用于实现了IDisposable接口的类型。



21.14 代

返回

代(generation)是垃圾回收器采用的一种机制,它唯一的目的是提升应用程序的性能。一个基于代得垃圾回收器做出了以下几点假设:

对象越新,生存期越短。

对象越老,生存期越长。

回收堆的一部分,速度快于回收整个堆。

CLR的托管对只支持三代:第0代、第1代和第2代。CLR初始化是,会为每一代做预算。预算的大小以提升性能为宜。预算越大,垃圾回收的频率越低。

假设第0代预算容量为256KB(顺便说一句,之所以第0代预算容量为256KB,是因为这些象都能装入CPU的L2缓存,使内存压缩能非常快的速度完成。),第1代预算容量为2M,第2代预算容量为10M。

一个新的初始化的堆,其中包含了一些对象,所有的对象都是第0代,垃圾回收尚未发生;

当第0代得对象刚好占用256K,又要分配对象时,垃圾回收器启动。垃圾回收器先判断哪些对象为垃圾(参考21.7),压缩可达对象。垃圾回收后,这些存活的对象被认为是第1代对象;

在第1代未满,第0代满时,垃圾回收器只垃圾回收器只回收第0代,见第2步;

当第0代满,第一代也满了,垃圾回收器会回收第1代,第0代。垃圾回收后,第0代的可达对象被提升至第1代,第1代的可达对象被提升至第2代.

CLR的垃圾回收器是自调节的。这意味着垃圾回收器会在执行垃圾回收的过程中了解应用程序的行为。如果垃圾回收期发现在回收0代存活下来的对象很少,就可能将第0代的预算从256KB减少至128KB。少的预算意味着垃圾回收更频繁的发生,但垃圾回收器需要做的工作会减少,从而减少进程的工作集。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: