1. 如何判断对象已死
1.1、引用计数法
在对象中添加一个引用计数器,每有代码引用它,计数器便加1,引用失效则减1。引用计数法在实现上相对简单,判定效率高。但在Java虚拟机里没有使用引用计数器算法进行内存管理,因为它难以解决对象间循环引用的问题。
public class Test{
public Object instance = null;
}
public static void testGC(){
Test objA = new Test();
Test objB = new Test();
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;//由于两个对象间相互引用,所以并不会将它们回收。
}
1.2、可达性分析算法
目前,主流程序语言都是通过可达性分析来判断对象是否存活。基本思想是通过一系列GC Root(GC根节点)起始,向下搜索,走过的路径称为引用链,当一个对象到根节点之间没有任何引用链相连,则这个对象是不可达的。
Java中可作为GC Root的对象包括:
- 虚拟机栈中的引用对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用对象
- 本地方法栈中引用对象
但是当一个对象为不可达时,也不一定就 ”非死不可“ ,至少需要经过两次标记过程
- 如果可达性分析后,对象没有与GCRoot相连接的引用链,则会进行第一次标记,然后根据对象是否有必要执行finalize()进行一次筛选,若对象没有覆盖finalize(),或者该对象的finalize()已经被虚拟机调用过了,则认为没有必要执行。
- 如果对象判定为有必要执行finalize()方法,会将其放置到F-Queue队列,并由Finalizer线程执行。GC将对F-Queue中对象进行第二次标记,如果对象重写的finalize()方法中重新与GCRoot建立引用链,那么再第二次标记时将被移出F-Queue,逃脱被回收的命运。,
2. 垃圾收集算法
2.1 标记—清除算法
对需要回收的对象进行标记,完成后扫描内存空间,将所有标记的对象进行回收
缺点:
- 标记和清除过程效率不高
- 回收之后产生大量不连续的内存碎片,导致在分配较大对象时找不到足够的连续空间,提前触发另一次垃圾收集动作。
2.2 复制算法
先将内存划分为等大的两部分,每次只使用其中一块,当一块内存用完时,采用可达行分析,将还存活的对象复制到另一块内存中,然后将之前的内存空间清空。
缺点:
每次只能使用一半的内存空间。
2.3 标记-整理算法
标记过程与标记清除算法一样,但后续步骤不是直接清理对象,而是将存活对象都移动到空闲的一端,并更新指针。
缺点
由于需要移动对象,成本更高。
2.5 JVM为了优化垃圾收集效率,采用分代收集算法。一般把Java堆划分为新生代和老年代。在新生代中每次GC都只有少量对象存活,因此使用复制算法提高效率。而老年代中,对象存活率高,使用标记-清除或者标记-整理算法