在虚拟机回收对象时首先要判断对象是否死亡,那么如何判断对象是否死亡呢?这里有两种算法实现:引用计数器法和可达性算法。

引用计数器法:

给对象添加一个引用计数器,每当有一个地方引用它时,计数器就加1;当引用失效时,计数器就减1;任何时刻计数器为0的对象就是不在被使用的。
优点: 实现简单,判定效率高,在大部分情况下都是一个不错的算法。
缺点: 无法解决对象循环引用的问题。

User userA = new User();
User userB = new User();
userA.eat() = userB;
userB.eat() = userA;//循环引用
userA=null;
userB=null;//将对象设为空,GC回收
System.gc;

上述代码对象userA和userB互相引用,所以如果用引用计数器法的话虚拟机并不会回收它们,这样下去就会导致内存崩溃。所以,jvm中使用了可达性算法来判断对象是否存活。

可达性算法:

这个算法的思想是通过一系列的”GC roots“ 的对象作为根节点,从这些节点开始向下搜索,搜索走过的路径称为”引用链“,当一个对象到GC Roots没有任何一个引用链相连,则证明这个对象是不可用的。
java解决新生代老年代互相引用问题,在程序执行时会专门开辟一块空间记录下来,称为rememberedSet,在新生代进行可达性分析时,以GC Roots和rememberedSet为依据。

java中可做为GC Roots的对象:

  1. 虚拟机栈中引用的对象。
  2. 方法区中类静态属性引用的对象。
  3. 方法区中常量引用的对象。
  4. 本地方法栈中引用的对象。

对象的拯救
在可达性算法中不可达的对象,不是非死不可的,这时候它们暂时处于”缓刑“阶段。要真正宣告一个对象死亡,至少要经过两次标记过程。如果对象在可达性分析之后没有发现与GC Roots相连接的引用链,那它将会被第一次标记并执行一次筛选是,筛选的条件是此对象是否有必须要执行的finalize方法。当对象没有重写finalize对法或finalize方法已经被虚拟机调用过,虚拟机将这两种情况视为”没有必要执行“,之后会回收它们。
如果这个对象被判定为有必要执行finalize方法,这个对象就会被放置在一个叫F-Queue的队列之中,并在稍后会由虚拟机自动创建一个低优先级的Finalizer线程去执行它。
这个线程执行只会触发这个方法,并不一定会等它运行结束,这样做的原因是:防止因为一个对象在finalze方法中执行缓慢或死循环,而导致F-Queue队列中其他对象处于永久等待状态,甚至导致整个内存回收系统崩溃。
finalize方法是对象逃脱死亡命运的最后一次机会,稍后GC将会对F-Queue队列中的对象进行第二次小规模的标记,如果对象在finalize方法中重新与引用链上的任何一个对象建立联系,就可以拯救自己。如:把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时,它将被移除”即将回收“的集合。否则,基本上它就被真正回收了。
下面是一段对象自我拯救的代码 尽量避免使用这种方法拯救对象

//gc运行后会执行且只执行一次finalize方法,回收那些没有被引用的对象,如果在finalize方法中对象链接上了gcroot就不会被回收。
public class Finalize {

    private static Finalize save_hook = null;//类变量

        public void isAlive(){
            System.out.println("yes,i am alive");
        }

        @Override
        public void finalize(){
            System.out.println("finalize method executed");
            Finalize.save_hook=this;
        }

        public static void main(String[] args) throws InterruptedException {

            save_hook = new Finalize();//对象
            //对象第一次拯救自己
            save_hook=null;
            System.gc();
            //暂停0.5秒等待他
            Thread.sleep(500);
        if(save_hook!=null){
            save_hook.isAlive();
        }else{
            System.out.println("no,i am dead");
        }

        //对象第二次拯救自己
        save_hook=null;
        System.gc();
        //暂停0.5秒等待他
        Thread.sleep(500);
        if(save_hook!=null){
            save_hook.isAlive();
        }else{
            System.out.println("no,i am dead");
        }
    }