jvm

垃圾收集与内存回收策略

2018-07-19  本文已影响13人  茶还是咖啡

判定一个对象是否是垃圾

引用计数法

给对象添加一个引用计数器,每当有一个地方去引用他,引用计数器 的值+1,引用失效时,引用计数器的值-1,引用计数器的值为0时,代表该对象不可能再被使用,应该被回收。
引用计数法没有解决对象循环引用的问题,导致JVM无法回收,造成内存泄漏。

可达性分析算法

GC Root的对象作为起点,从这些节点向下搜索,搜索走过的路径成为引用链,当一个对象没有任何引用链相连时代表对象不可用。


GC Root的对象

引用

        Object obj = new Object();
        SoftReference<Object> objectSoftReference = new SoftReference<>(obj);
        obj=null;
        Object obj = new Object();
        WeakReference<Object> objectWeakReference = new WeakReference<>(obj);
        obj=null;

对象的自救


如果对象在经过可达性分析后没有与GC Root相关联,进行第一次标记,并进行一次筛选,如果该对象的finalized方法被覆盖并且finalized方法从未被执行,判定对象有必要执行finalized方法,该对象会被放到一个叫F-Queue队列中,并且JVM会开启一个低优先级的finalized线程去执行该队列中对象的finalized方法,如果对象中finalized执行后,该对象重新被引用上了,该对象自救成功,JVM不会对该对象进行回收,但是finalized线程是一个优先级比较低的线程,所以可能有一些对象的finalized方法还没有执行就被GC了。


对象zi救Demo

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 execute!");
        FinalizeEscapeGC.SAVE_HOOK=this;
    }

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

        //first save self
        SAVE_HOOK = null;
        System.gc();
        //Finalizer线程优先级较低,等待一会,保证足够的时间去执行
        Thread.sleep(500);
        if(SAVE_HOOK!=null){
            SAVE_HOOK.isAlive();
        }else {
            System.out.println("no i am dead =_=");
        }

        //second save self
        SAVE_HOOK = null;
        System.gc();
        //Finalizer线程优先级较低,等待一会,保证足够的时间去执行
        Thread.sleep(500);
        if(SAVE_HOOK!=null){
            SAVE_HOOK.isAlive();
        }else {
            System.out.println("no i am dead:=_=");
        }
    }
}

运行结果

finalize method execute!
yes i am still alive:~_~
no i am dead:=_=

可以看出对象只能通过finalized方法自救一次,因为finalized方法JVM只会自动调用一次。

回收方法区

方法区又名永久代,意为很少有资源被回收。主要存放编译后的代码,类信息,常量,静态变量等。
JVM主要回收方法区中废弃的常量和无用的类(字节码文件)

垃圾收集算法

标记-清除


存在的问题:
1.标记和清除的效率都不高。
2.标记清除之后,会出现大量不连续的内存碎片,可能导致大对象无法分配。进而提前出发GC。

复制算法


新生代和老年代的大小一般不会相等,堆内存被分为两个Eden和一个Survivor,默认比例为1:1:8

标记-整理

和标记-清除算法类似,但是后续步骤不是直接对对象进行清除,而是将所有存活的对象都移动到一端,然后直接清理端边界以外的内存。
解决了标记-清除对象回收后内存空间不连续的问题,但是增加了对象的移动,效率会略有降低。

分代收集

根据对象的生命周期不同,将堆内存分为新生代和老年代,(新生代的对象大多朝生夕死,老年代的对象存活时间较长)使用不同的垃圾回收算法,新生代使用复制算法,老年代使用标记-清除 或者 标记-整理。

上一篇下一篇

猜你喜欢

热点阅读