GC(3)、Java对象回收概述

一、被可达性分析或者引用计数法标记为无用对象后就一定会被GC回收掉吗?

二、答案(纯文字,希望大家好好读,通俗易懂,很关键)
当然不是!!!!
一个对象的死亡至少要经历两次标记过程:
如果对象被标记为可回收对象,那他将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法,当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行回收”。(这也是为什么大家不鼓励重写finalize方法的原因之一)

如果对象被判定为有必要执行finalize()方法,那么这个对象会放到一个叫做F-Queue队列中,并在稍后一个由虚拟机自动建立的、低优先级的Finalizer线程去执行它,这里的执行只是负责触发这个方法,并不一定等这个方法执行结束。这样做的原因是如果一个对象在finalize()方法中执行缓慢,或者发生了死循环,将很可能导致F-Queue队列中其他对象处于永久等待,甚至整个内存回收系统崩溃。

finalize()方法是对象逃脱死亡命运的最后你一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可(这里描述的是可达性分析算法,因为虚拟机几乎都不采取引用计数法)。譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那么在第二次标记时他将被移除出“即将回收”的队列;若对象这时候还没有逃脱,那么基本上真的就要被回收了。

三、用代码证明上面的答案

/**
 * 1、对象在被GC时候可以有一次自我挽救的机会
 * 2、这种自救的机会只有一次,因为一个对象的finalize方法最多只能被系统自动调用1次
 *
 * @author TongWei.Chen 2017-09-05 14:49:04
 */
public class FinalizeEscapeGC {

    public static FinalizeEscapeGC SAVE_HOOK = null;

    public void isAlive() {
        System.out.println("我还活着呢....");
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize方法执行了....");
        FinalizeEscapeGC.SAVE_HOOK = this;
    }

    public static void main(String[] args) throws InterruptedException {
        SAVE_HOOK = new FinalizeEscapeGC();

        //对象第一次成功拯救自己
        SAVE_HOOK = null;
        //督促垃圾收集器工作
        System.gc();

        //因为finalize方法优先级很低,所以暂停1s以等待它
        Thread.sleep(1000);
        if(SAVE_HOOK != null) {
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("我已经死了....");
        }

        //下面这段代码与上面的完全相同,但是这次自救却失败了。

        //对象第二次成功拯救自己
        SAVE_HOOK = null;
        System.gc();

        //因为finalize方法优先级很低,所以暂停1s以等待它
        Thread.sleep(1000);
        if(SAVE_HOOK != null) {
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("我已经死了....");
        }
    }
}

结果

finalize方法执行了....
我还活着呢....
我已经死了....

四、回收方法区
GC回收的不只是堆内存,还有方法区!!!
永久代(方法区)的垃圾收集主要回收两部分内容:废弃常量和无用的类。

判定一个常量是否是“废弃常量”很简单:
比如

String str = "abc";

abc在常量池中,但是现在str = null;不在引用abc这个常量了,那么这是发生内存回收的话,就会把abc清除常量池。

判定一个是否是“无用的类”很麻烦:

1、该类所有的对象都已经回收,也就是Java堆中不存在该类的任何对象。
2、加载该类的ClassLoader已经被回收。
3、该类对应的Class对象没有在任何地方被引用,无法再任何地方通过反射访问该类的方法。

若有兴趣,欢迎来加入群,【Java初学者学习交流群】:458430385,此群有Java开发人员、UI设计人员和前端工程师。有问必答,共同探讨学习,一起进步!
欢迎关注我的微信公众号【Java码农社区】,会定时推送各种干货:


GC(3)、Java对象回收概述_第1张图片
qrcode_for_gh_577b64e73701_258.jpg

你可能感兴趣的:(GC(3)、Java对象回收概述)