一、对象何时可回收:
由于JAVA堆中存放着应用中的大部分对象实例,垃圾收集器在对堆进行收集前需要确定哪些对象实例应该回收,哪些不需要,这时就有相关的判断对象是否可回收的方法:引用计数算法、根搜索算法,其中后者是主流算法。这个算法的基本思路是通过一系列的GC ROOT对象作为搜索起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当从GC ROOT到某一个对象不可达时,则证明此对象是可回收的。
在JAVA里,可作为GC ROOT的对象包括下面几种:
》虚拟机栈中引用的对象
》方法区中的类静态属性引用的对象
》方法区中的常量引用的对象
》本地方法栈中JNI(即一般说的Native方法)引用的对象
在根搜索算法中不可达的对象,并不会马上被回收,而是由JVM通过如下规则对该对象进行处理:
如果该对象的finalize()方法没有被override 或者 finalize()方法已经被JVM执行过,则该对象将直接进入“即将回收”的集合中;
反之,则JVM将会把该对象放入一个名为F-Queue的队列之中,并在稍后由一JVM自动创建的低优先级的线程去遍历队列中的每个对象,并调用该对象的finalize()方法(该线程并不承诺会等待该方法运行结束,防止线程阻塞而导致JVM崩溃);而在对象的finalize()方法执行过程中,如果该对象重新与某一GC ROOT对象建立关联(即又变路径可达),那么该对象将“复活”,即它将不会被放入“即将回收”的集合中。
通过上面分析,我们可以知道一状况:任何JAVA对象的finalize()方法只会被JVM执行一次。但由于其运行代价高昂,不确定性太大,无法保证程序稳定性及性能,因此程序中尽量不override该方法,尽可忘掉它。而用try...catch...finally...代替。
二、对象如何回收
对象如何回收其实说的就是垃圾收集算法,当前主要的垃圾收集算法有:
1 标记--清除算法:简单讲,就是先找出所有可回收对象并标记,然后回收。缺点碎片多
2 复制算法:内存一分为二,一块平时使用,一块保留经供复制用。在回收时,把存活对象复制至保留区域,把平时使用区域清空,并全使二者功能转换。
3 标记--整理算法:简单讲,就是先找出所有可回收对象并标记,然后回收,最后把存活对象整理至一整块的连续内存区域,使空闲空间也为连续。
4 分代收集算法:前面3种算法的综合。根据对象的存活周期的不同把内存划分为几块,一般是把JAVA堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次收集时都发现有大批对象可回收,只有少量存活,那就选用”复制算法“,只需要付出少量内存空间成本就可以完成回收。而老年代中因为对象存活率高,没有额外空间对它进行分配担保,就必须使用”标记--清理算法”或者“标记--整理算法“来进行回收了。
分代收集算法是现在的商业虚拟机使用的垃圾回收算法。IBM的研究机构表明,新生代中的对象98%是朝生夕死的,所以使用复制算法时并不需要按照1:1的比例来划分内存空间,而是将内存划分为一块较大的Eden空间和两块空间较小的Survivor空间,每次使用Eden和其中的一块Survivor(FromSurvivor),当回收时,将Eden和Survivor中存活的对象一次性复制到另外一块Survivor空间中,最后清理掉Eden和刚才用过的Survivor(FromSurvivor)的空间。HotSpot虚拟机默认Eden和FromSurvivor的大小比例是8:1,剩余的10%的空间当作保留区域,这样将大大提高内存空间(即JAVA堆)的利用率。