垃圾回收
本节将介绍以下内容:
— .NET垃圾回收机制
—非托管资源的清理
引言
.NET自动内存管理将开发人员从内存错误的泥潭中解放出来,这一切都归功于垃圾回收(GC,Garbage Collection)机制。
通过对对象创建全过程的讲述,我们理解了CLR执行对象内存分配的基本面貌。一个分配了内存空间和完成初始化的对象实例,就是一个CLR世界中的新生命体,其生命周期大概可以概括为:对象在系统中进行一定的操作和应用,到一定阶段它将不被系统中任何对象引用或操作,则表示该对象不会再被使用。因此,对象符合了可以销毁的条件,而CLR可能不会马上执行销毁操作,而是在适当的时间执行该对象的内存销毁。一旦被执行销毁,对象及其成员将不可在运行时使用,最后由垃圾收集器释放其内存资源,完成一个对象由生而灭的全过程。
由此可见,中自动内存管理是由垃圾回收器来执行的,GC自动完成对托管堆的全权管理,然而一股脑将所有事情交给GC,并非万全保障。基于性能与安全的考虑,很有必要对GC的工作机理、执行过程,以及对非托管资源的清理做一个讨论。
垃圾回收
顾名思义,垃圾回收就是清理内存中的垃圾,因此了解垃圾回收机制就应从以下几个方面着手:
l 什么样的对象被GC认为是垃圾呢?
l 如何回收?
l 何时回收?
l 回收之后,又执行哪些操作?
清楚地回答上述几个问题,的垃圾回收机制。下面本节就逐一揭开这几个问题的答案。
l 什么样的对象被GC认为是垃圾呢?
简单地说,一个对象成为“垃圾”就表示该对象不被任何其他对象所引用。因此,GC必须采用一定的算法在托管堆中遍历所有对象,最终形成一个可达对象图,而不可达的对象将成为被释放的垃圾对象等待收集。
l 如何回收?
每个应用程序有一组根(指针),根指向托管堆中的存储位置,由JIT编译器和CLR运行时维护根指针列表,主要包括全局变量、静态变量、局部变量和寄存器指针等。下面以一个简单的示例来说明,GC执行垃圾收集的具体过程。
class A
{
private B objB;
public A(B o)
{
objB = o;
}
~A()
{
("Destory A.");
}
}
class B
{
private C objC;
public B(C o)
{
objC = o;
}
~B()
{
("Destory B.");
}
}
class C
{
~C()
{
("Destory C.");
}
}
public class Test_GCRun
{
public static void Main()
{
A a = new A(new B(new C()));
//强制执行垃圾回收
(0);
();
}
}
在上述执行中,当创建类型A的对象a时,在托管堆中将新建类型B的实例(假设表示为objB)和类型C的实例(假设表示为objC),并且这几个对象之间保存着一定的联系。而局部变量a则相当于一个应用程序的根,假设其在托管堆中对应的实例表示为objA,则当前的引用关系可以表示为图5-6。
图5-6 垃圾收集执行前的托管堆
垃圾收集器正是通过根指针列表来获得托管堆中的对象图,其中定义了应用程序根引用的托管堆中的对象,当垃圾收集器启动时,它假设所有对象都是可回收的垃圾,并开始遍历所有的根,将根引用的对象标记为可达对象添加到可达对象图中,在遍历过程中,如果根引用的对象还引用着其他对象,则该对象也被添加到可达对象图中,依次类推,垃圾收集器通过根列表的递归遍历,将能找到所有可达对象,并形成一个可达对象图。同时那些不可达对象则被认为是可回收对象,垃圾收集器接着运行垃圾收集进程来释放垃圾对象的内存空间。通常,将这种收集算法称为:标记和清除收集算法。
在上例中,a可以看出是应用程序的一个根,它在托管堆中对应的对象objA就是一个可达对象,而对象objA依次关联的objB、objC都是可达对象,被添加到可达对象图中。当Main方法运行结束时,a不再被引用,则其不再是一个根,此时通过
垃圾回收 来自淘豆网m.daumloan.com转载请标明出处.