Java和.Net垃圾回收机制比较
1.什么是垃圾回收机制
找到没有被引用的对象,并将其回收。
具体的过程也就是上面所说两个大的步骤,先找没有被引用的对象,再回收它们。
下面分别针对上述两个步骤,详细描述Java和.Net的具体实现
2.查找没有被引用的对象
2.1.引用计数法
这个算法的实现是,给对象中添加一个引用计数器,每当一个地方引用这个对象时,计数器值+1;当引用失效时,计数器值-1。任何时刻计数值为0的对象就是不可能再被使用的。
2.2.可达性分析法
这个算法的基本思想是通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。
图中obj8/obj9/obj10是不可达的对象,需要被回收。
Java和.Net均采用了该算法来查找未被引用对象,但两者的GCRoots不同:
Java | .Net |
---|---|
虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。2. 方法区中的类静态属性引用的对象。3. 方法区中常量引用的对象。4. 本地方法栈中JNI(Native方法)引用的对象。 | 1. 全局对象和静态对象的引用2. 应用程序代码库中局部对象的引用3. 传递进一个方法的对象参数的引用4. 等待被终结(finalize,后面介绍)对象的引用5. 任何引用对象的CPU寄存器 |
3.垃圾回收算法
3.1…NET垃圾回收过程
.NET框架包含一个托管堆,所有的.NET语言在分配引用类型对象时都要使用它。像值类型这样的轻量级对象始终分配在栈中,但是所有的类实例和数组都被生成在一个内存池中,这个内存池就是托管堆。
.NET框架中的垃圾回收器被称为分代的垃圾回收器(Generational Garbage Collector),也就是说被分配的对象划分为3个类别,或称为“代”,分别为0,1,2。
最近被分配内存空间的对象被放置于第0代,因为第0代很小,小到足以放进处理器的二级(L2)缓存,所以第0代能够为我们提供对其中对象的快速存取;经过一轮垃圾回收后,仍然保留在第0代中的对象被移进第1代中;再经过一轮垃圾内存回收后,仍然保留在第1代中的对象则被移进第2代中。第2代包含了生存期较长的对象,这些对象至少经过了两轮回收。
垃圾回收器会先检查所有的0代对象,如果还需要更多的空间,那么垃圾回收器会继续检查所有 的1代对象,整理出足够的空间。这时,没有被回收的1代对象将成为2代对象。
分代可以避免每次垃圾回收都遍历整个托管堆,这样可以提高垃圾回收的性能。
3.2.Java垃圾回收过程
JVM使用分代收集器,它把堆分为三个主要的域:新域(Young Generation)、旧域(Tenured Generation)以及永久域;
JVM生成的所有新对象放在新域中;一旦对象经历了一定数量的垃圾收集循环后,便获得使用期并进入旧域;
上图展示了7种作用于不同分代的收集器,如果两个收集器之间存在连线,那说明它们可以搭配使用。
虚拟机所处的区域说明它是属于新域收集器还是旧域收集器,可以根据应用选择最合适的收集器。
4.总结
经过上述分析描述,我们可以看到,在垃圾回收大的步骤及算法上,Java和.Net是一致的,体现在:
查找未被引用对象的算法采用可达性分析法;垃圾回收均采用分代垃圾回收法。
但在实现细节上有细微区别:
比 较 内 容 | Java | .NET |
---|---|---|
可达性分析法根对象不同 | 1.虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。2. 方法区中的类静态属性引用的对象。3. 方法区中常量引用的对象。4. 本地方法栈中JNI(Native方法)引用的对象。 | 1. 全局对象和静态对象的引用2. 应用程序代码库中局部对象的引用3. 传递进一个方法的对象参数的引用4. 等待被终结(finalize,后面介绍)对象的引用5. 任何引用对象的CPU寄存器 |
分代垃圾回收法实现细节不同 | 分为新旧域,并有多个采集器可以选择,实现逻辑参考3.2节 | 分为0-2三个域,实现逻辑参考3.1节 |