垃圾回收GC:.Net自动内存管理 上(二)内存算法
- 垃圾回收GC:.Net自动内存管理 上(一)内存分配
-
垃圾回收GC:.Net自动内存管理 上(二)内存算法
前言
.Net下的GC完全解决了开发者跟踪内存使用以及控制释放内存的窘态。然而,你或午想要理解GC是怎么工作的。此系列文章中将会解释内存资源是怎么被合理分配及管理的,并包含非常详细的内在算法描述。同时,还将讨论GC的内存清理流程及什么时清理,怎么样强制清理。
内存算法
深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第六节 理解垃圾回收GC,提搞程序性能
。下图是执行垃圾回收之后的托管堆:
在所有垃圾对象被回收后,所有非垃圾对象重新变得紧凑,非垃圾对象指针也会全部被修复,NextObjPtr会被放到最后一个非垃圾对象之后。这时,new操作符又开始偿试创建新对象,程序请求的资源也被成功创建。
你可以看到,GC产生了一次明显的性能消耗,这是使用GC的主要缺点。然而,记住GC仅在堆被占满的情况进行回收,在回收之前,托管堆明显比C语言运行时的堆快很多。GC还提供了一些优化操作可以大大提高垃圾回收的性能。后续文章会讨论GC怎么优化垃圾回收的。
有一些很重要的点要指出来。你不再需要通过写代码去管理程序资源的寿命,文中一开始提到的两种BUG也将不再存在。首先,它不可能再产生资源泄漏,因为任何程序根节点(roots)访问不到的资源(即垃圾)都会被回收。其次,你也不再可能访问一个被释放掉的资源,因为如果资源能够被访问到,它就永远不会被释放。如果资源不能够被访问到,我们也没有理由去访问它。
下面代码展示了资源是怎么样被分配与管理的:
class Application { public static int Main(String[] args) { //在堆中创建ArrayList对象,myArray现在作为程序根节点 ArrayList myArray = new ArrayList(); // 在堆中创建10000个对象 for (int x = 0; x < 10000; x++) { myArray.Add(new Object()); } // 现在,myArray是一个根(在线程栈里)。 // 因此,myArray和10000个对象都是可达的 Console.WriteLine(myArray.Length); // 在代码中myArray最后一个引用(Console.WriteLine(myArray.Length))之后, myArray 不再是一个根 // 不必非要等到此方法返回后,JIT编译器会知道在myArray最后一个引用之后把它标识为非根节点 // 因为myArray不再是根节点,所有10001个对象不再可达 // 它们会被看作是垃圾 // 但是它们会一直存在直到GC对它们进行回收 } }
如果GC这么出色,为什么C++不使用它呢?原因是GC必须能够识别程序根节点(roots),并且必须能够找到所有对象指针。C++里允许指针进行类型转换,所以不可能确定一个指针指向的是什么。在一般语言运行时CLR中,托管堆总是能够确定对象的确切类型,并可通过元数据metadata信息决定一个对象的成员指向哪些其它对象。