垃圾收集算法

2019-01-31  本文已影响0人  永远的太阳0123

1 判断对象是否存活


2 四种引用

public class StrongReferenceTest {

    public static int M = 1024 * 1024;

    public static void printlnMemory(String tag) {
        Runtime runtime = Runtime.getRuntime();
        int M = StrongReferenceTest.M;
        System.out.println("\n" + tag + ":");
        System.out.println(runtime.freeMemory() / M + "M(free)/" + runtime.totalMemory() / M + "M(total)");
    }

    public static void main(String[] args) {
        StrongReferenceTest.printlnMemory("1.原可用内存和总内存");
        // 实例化10M的数组,和strongReference建立强引用
        byte[] strongReference = new byte[10 * StrongReferenceTest.M];
        StrongReferenceTest.printlnMemory("2.实例化10M的数组,建立强引用");
        System.out.println("strongReference : " + strongReference);
        System.gc();
        StrongReferenceTest.printlnMemory("3.GC之后");
        System.out.println("strongReference : " + strongReference);
        // 断开强引用
        strongReference = null;
        StrongReferenceTest.printlnMemory("4.断开强引用");
        System.out.println("strongReference : " + strongReference);
        System.gc();
        StrongReferenceTest.printlnMemory("5.GC之后");
        System.out.println("strongReference : " + strongReference);
    }

}
1.原可用内存和总内存:
14M(free)/15M(total)

2.实例化10M的数组,建立强引用:
4M(free)/15M(total)
strongReference : [B@1b6d3586

3.GC之后:
4M(free)/15M(total)
strongReference : [B@1b6d3586

4.断开强引用:
4M(free)/15M(total)
strongReference : null

5.GC之后:
14M(free)/15M(total)
strongReference : null
public class SoftReferenceTest {

    public static int M = 1024 * 1024;

    public static void printlnMemory(String tag) {
        Runtime runtime = Runtime.getRuntime();
        int M = StrongReferenceTest.M;
        System.out.println("\n" + tag + ":");
        System.out.println(runtime.freeMemory() / M + "M(free)/" + runtime.totalMemory() / M + "M(total)");
    }


    public static void main(String[] args) {
        SoftReferenceTest.printlnMemory("1.原可用内存和总内存");
        //建立软引用
        SoftReference<Object> softReference = new SoftReference<>(new byte[10 * SoftReferenceTest.M]);
        SoftReferenceTest.printlnMemory("2.实例化10M的数组,建立软引用");
        System.out.println("softReference.get() : " + softReference.get());
        System.gc();
        SoftReferenceTest.printlnMemory("3.内存可用容量充足,GC后");
        System.out.println("softReference.get() : " + softReference.get());
        // 实例化一个4M的数组,使内存不够用,建立软引用
        // GC后10M的数组被回收
        SoftReference<Object> softReference2 = new SoftReference<>(new byte[4 * SoftReferenceTest.M]);
        SoftReferenceTest.printlnMemory("4.实例化4M的数组,建立软引用");
        System.out.println("softReference.get() : " + softReference.get());
        System.out.println("softReference2.get() : " + softReference2.get());
    }

}
1.原可用内存和总内存:
14M(free)/15M(total)

2.实例化10M的数组,建立软引用:
4M(free)/15M(total)
softReference.get() : [B@1b6d3586

3.内存可用容量充足,GC后:
4M(free)/15M(total)
softReference.get() : [B@1b6d3586

4.实例化4M的数组,建立软引用:
10M(free)/15M(total)
softReference.get() : null
softReference2.get() : [B@4554617c
public class WeakReferenceTest {

    public static int M = 1024 * 1024;

    public static void printlnMemory(String tag) {
        Runtime runtime = Runtime.getRuntime();
        int M = WeakReferenceTest.M;
        System.out.println("\n" + tag + ":");
        System.out.println(runtime.freeMemory() / M + "M(free)/" + runtime.totalMemory() / M + "M(total)");
    }


    public static void main(String[] args) {
        WeakReferenceTest.printlnMemory("1.原可用内存和总内存");
        //创建弱引用
        WeakReference<Object> weakReference = new WeakReference<Object>(new byte[10 * WeakReferenceTest.M]);
        WeakReferenceTest.printlnMemory("2.实例化10M的数组,并建立弱引用");
        System.out.println("weakReference.get() : " + weakReference.get());
        System.gc();
        StrongReferenceTest.printlnMemory("3.GC后");
        System.out.println("weakReference.get() : " + weakReference.get());
    }

}
1.原可用内存和总内存:
14M(free)/15M(total)

2.实例化10M的数组,并建立弱引用:
4M(free)/15M(total)
weakReference.get() : [B@1b6d3586

3.GC后:
14M(free)/15M(total)
weakReference.get() : null
public class PhantomReferenceTest {

    public static int M = 1024 * 1024;

    public static void printlnMemory(String tag) {
        Runtime runtime = Runtime.getRuntime();
        int M = PhantomReferenceTest.M;
        System.out.println("\n" + tag + ":");
        System.out.println(runtime.freeMemory() / M + "M(free)/" + runtime.totalMemory() / M + "M(total)");
    }

    public static void main(String[] args) throws InterruptedException {
        PhantomReferenceTest.printlnMemory("1.原可用内存和总内存");
        byte[] object = new byte[10 * PhantomReferenceTest.M];
        PhantomReferenceTest.printlnMemory("2.实例化10M的数组后");
        // 建立虚引用
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        PhantomReference<Object> phantomReference = new PhantomReference<>(object, referenceQueue);
        PhantomReferenceTest.printlnMemory("3.建立虚引用后");
        System.out.println("phantomReference : " + phantomReference);
        System.out.println("phantomReference.get() : " + phantomReference.get());
        System.out.println("referenceQueue.poll() : " + referenceQueue.poll());
        // 断开强引用
        object = null;
        PhantomReferenceTest.printlnMemory("4.执行object = null;强引用断开后");
        System.gc();
        PhantomReferenceTest.printlnMemory("5.GC后");
        System.out.println("phantomReference : " + phantomReference);
        System.out.println("phantomReference.get() : " + phantomReference.get());
        System.out.println("referenceQueue.poll() : " + referenceQueue.poll());
        //断开虚引用
        phantomReference = null;
        System.gc();
        PhantomReferenceTest.printlnMemory("6.断开虚引用后GC");
        System.out.println("phantomReference : " + phantomReference);
        System.out.println("referenceQueue.poll() : " + referenceQueue.poll());
    }

}
1.原可用内存和总内存:
14M(free)/15M(total)

2.实例化10M的数组后:
4M(free)/15M(total)

3.建立虚引用后:
4M(free)/15M(total)
phantomReference : java.lang.ref.PhantomReference@1b6d3586
phantomReference.get() : null
referenceQueue.poll() : null

4.执行object = null;强引用断开后:
4M(free)/15M(total)

5.GC后:
4M(free)/15M(total)
phantomReference : java.lang.ref.PhantomReference@1b6d3586
phantomReference.get() : null
referenceQueue.poll() : java.lang.ref.PhantomReference@1b6d3586

6.断开虚引用后GC:
14M(free)/15M(total)
phantomReference : null
referenceQueue.poll() : null

3 finalize方法

即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。

如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会放置在一个叫做F-Queue的队列之中,并在稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程去执行它。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束,这样做的原因是,如果一个对象在finalize()方法中执行缓慢,或者发生了死循环(更极端的情况),将很可能会导致F-Queue队列中其他对象永久处于等待,甚至导致整个内存回收系统崩溃。finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那基本上它就真的被回收了。

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 method executed!");
        FinalizeEscapeGC.SAVE_HOOK = this;// 这里的this是指已经进入F-Queue的那个FinalizeEscapeGC对象
        System.out.println(SAVE_HOOK);
    }

}
public class FinalizeEscapeGCTest {

    public static void main(String[] args) throws Throwable {
        FinalizeEscapeGC.SAVE_HOOK = new FinalizeEscapeGC();
        System.out.println(FinalizeEscapeGC.SAVE_HOOK);
        // 对象第一次成功拯救自己
        FinalizeEscapeGC.SAVE_HOOK = null;
        System.gc();
        // 因为finalize方法优先级很低,所以暂停0.5秒以等待它
        Thread.sleep(500);
        if (FinalizeEscapeGC.SAVE_HOOK != null) {
            FinalizeEscapeGC.SAVE_HOOK.isAlive();
        } else {
            System.out.println("no, i am dead");
        }
        // 下面这段代码与上面的完全相同,但是这次自救却失败了
        FinalizeEscapeGC.SAVE_HOOK = null;
        System.gc();
        // 因为finalize方法优先级很低,所以暂停0.5秒以等待它
        Thread.sleep(500);
        if (FinalizeEscapeGC.SAVE_HOOK != null) {
            FinalizeEscapeGC.SAVE_HOOK.isAlive();
        } else {
            System.out.println("no, i am dead");
        }
    }
}
FinalizeEscapeGC@1b6d3586
finalize method executed!
FinalizeEscapeGC@1b6d3586
yes, i am still alive
no, i am dead

任何一个对象的finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()方法不会被再次执行,因此第二段代码的自救行动失败了。

我们应该尽量避免使用finalize()方法。


4 回收方法区

永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。

回收废弃常量与回收Java堆中的对象非常类似。以常量池中字面量的回收为例,假如一个字符串“abc”已经进入了常量池中,但是当前系统没有任何一个String对象是叫做“abc”的,换句话说,就是没有任何String对象引用常量池中的“abc”常量,也没有其他地方引用了这个字面量,如果这时发生内存回收,而且必要的话,这个“abc”常量就会被系统清理出常量池。常量池中的其他类(接口)、方法、字段的符号引用也与此类似。

判定一个常量是否是“废弃常量”比较简单,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。类需要同时满足下面3个条件才能算是“无用的类”:
(1)该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。
(2)加载该类的ClassLoader已经被回收。
(3)该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
虚拟机可以对满足上述3个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样,不使用了就必然会回收。

在大量使用反射、动态代理、CGLib等ByteCode框架、动态生成JSP以及OSGi这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出。


5 垃圾收集算法

上一篇 下一篇

猜你喜欢

热点阅读