System.gc

来源:互联网 发布:怎么修改淘宝账号名称 编辑:程序博客网 时间:2024/05/21 17:22

测试一

import org.junit.Test;class Demo {    @Override    protected void finalize() throws Throwable {        super.finalize();        System.out.println("Demo::finalize");    }}public class T {    @Test    public void test() throws InterruptedException {        Demo demo = new Demo();        demo = null;        System.gc();    }}

大部分时候输出
here
Demo::finalize
偶尔出现输出
Demo::finalize
here
System.gc(); 后加上Thread.sleep(500); 则一直输出
Demo::finalize
here
隐约可以感觉到垃圾回收是一个与主线程独立的另一个线程完成。
finalize也就是一个回调函数。其实构造/析构函数本质上就是一个回调函数。

测试二
finalize 方法从protected 权限修饰符改为public
我们就可以显式的调用该方法
但是会有警示:

Reports any call of Object.finalize(). Calling Object.finalize() explicitly is a very bad idea, as it can result in objects being placed in an inconsistent state. Calls to super.finalize() from within implementations of finalize() are benign, and are not reported by this inspection.

即不建议显式调用。

测试三

while (true) {    new Demo();}

也会导致finalize 方法的执行。即不需要System.gc ,也会触发。


看到一个解释:
1. 编译时不对复写了finalize()方法的类做特殊处理,运行时加载该类会被检查出复写了finalize(),然后设置标志位has_finalizer。
2. 创建对象时不但创建一个该类对象,同时创建一个Finalizer对象,并被Finalizer对象引用, 然后Finalizer对象被加入到Finalizer.unfinalized队列中。
3. gc时发现Finalizer.unfinalized中有需要执行finalize()的对象,就会将其加入到Reference.pending链表中。
4. 通过Reference.ReferenceHandler线程将Reference.pending链表加入到Finalizer.queue中。
5. 通过Finalizer.FinalizerThread线程将queue数据poll出来并执行finalize()方法。
6. 执行了finalize()的对象被移除Finalizer.unfinalized队列。

这里写图片描述
上图为Demo类字节码文件中对应的两个方法。

    /**     * Runs the garbage collector.     * <p>     * Calling the <code>gc</code> method suggests that the Java Virtual     * Machine expend effort toward recycling unused objects in order to     * make the memory they currently occupy available for quick reuse.     * When control returns from the method call, the Java Virtual     * Machine has made a best effort to reclaim space from all discarded     * objects.     * <p>     * The call <code>System.gc()</code> is effectively equivalent to the     * call:     * <blockquote><pre>     * Runtime.getRuntime().gc()     * </pre></blockquote>     *     * @see     java.lang.Runtime#gc()     */    public static void gc() {        Runtime.getRuntime().gc();    }

如注释所述,System.gc是一个尽力而为的方法,当然什么都不干也行。

这里写图片描述

对象自我拯救事例:

public class FinalizeEscapeGC {    public static FinalizeEscapeGC SAVE_HOOK = null;    public void isAlive() {        System.out.println("yes, i am still alive :)");    }    @Override    protected void finalize() throws Throwable {        super.finalize();        System.out.println("finalize mehtod executed!");        FinalizeEscapeGC.SAVE_HOOK = this;    }    public static void main(String[] args) throws Throwable {        SAVE_HOOK = new FinalizeEscapeGC();        //对象第一次成功拯救自己        SAVE_HOOK = null;        System.gc();        // 因为Finalizer方法优先级很低,暂停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();        // 因为Finalizer方法优先级很低,暂停0.5秒,以等待它        Thread.sleep(500);        if (SAVE_HOOK != null) {            SAVE_HOOK.isAlive();        } else {            System.out.println("no, i am dead :(");        }    }}

输出
finalize mehtod executed!
yes, i am still alive :)
no, i am dead :(

从输出结果可以看出,SAVE_HOOK 对象的finalize()方法确实被GC收集器触发过,并且在被收集前成功逃脱了。
任何一个对象的finalize()方法都只会被系统调用一次,如果对象面临下一次回收,它的finalize()方法不会被再次执行,因此第二段代码的自救行动失败了。

这里写图片描述

另外新版本的Java将会废弃Object.finalize(),并添加新的java.lang.ref.Cleaner.)

原创粉丝点击