JVM--GC垃圾回收器

来源:互联网 发布:中信银行网络在线客服 编辑:程序博客网 时间:2024/05/17 22:45

概述:java虚拟机对内存处理上主要做两件事,为对象分配内存和回收废弃的对象占用的内存,根据java的内存中的区域划分可以了解,JVM中主要占内存的是堆,因此GC主要处理的也是堆的回收。

1、如何判断哪些对象已废弃不用?

    (1)引用计数算法。

               即在创建对象的时候会给该对象维持一个引用计数器,当引用一次则+1,引用失效则-1,在许多语言中都用

        的是引用计数算法来清理内存,然而在java中却无法解决循环依赖的问题。

     (2)可达性分析算法

               以一系列的“GC ROOT”对象为起始点,向下搜索,如果可以到达某个对象,则不销毁,如果任何一个GC 

        ROOT对象都不能到达某对象,则认为此对象不可用(一般情况下,并没有立刻销毁,仅做了标记)。

可作为GC ROOT对象有:

虚拟机栈(栈帧中的局部变量表)中引用的对象

方法区中的类静态属性引用的对象

方法区中常量引用的对象

本地方法栈中引用的对象

可以看到这些对象都是内存中线程私有的,这些区域中内存的生命周期都是跟线程相同的,该内存存在,则

说明线程并未结束,而未结束的线程在引用堆中的某个对象,这个对象当然不是不可用的状态。

(3)对象的销毁

在可达性分析算法中,判断某个对象不可达时,并没有立即销毁对象,而是会对它进行一次标记和一次筛选,

筛选分两个条件,首先判断该对象是否覆盖了finalize()方法,然后判断finalize()方法是否被虚拟机调用过,若有则

不执行销毁程序,而是将它放入一个名为F-Queue的队列中。稍后GC会对F-Queue队列中的对象进行第二次小规模

的标记,如果在调用了finalize()方法的对象又一次被引用,则放弃执行,并移出垃圾回收集合,否则执行。

注意:在执行销毁程序的时候,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 :(

                以上代码需注意一点:两段代码相同,但是一次自救成功,一次自救失败,是因为finalize()方法只会被

        虚拟 机用一次,当面临下一次回收时,finalize()方法不会被执行,被直接销毁了。

        2、回收方法区。

               方法区在一些人认为是永久代,是永远不会被销毁的区域,在JVM规范中也有说可以不要求回收方法

        区,因为在方法区中的回收效率一般比较低。在堆的新生代中一般一次回收能回收70%~95%的空间,然而在方          法区却远达不到这个效率。

                在方法区中回收的主要是废弃常量和无用的类,回收废弃常量的模式与以可达性回收堆差不多。

        3、垃圾回收算法。

                (1)标记--清除算法。即对不可用的对象进行标记,然后清除,会造成内存空间不连续。

(2)复制算法。在HotSpot虚拟机中,是将内存分为一个Eden空间和两个surivivor空间,每次使用Eden空间和

一块surivivor空间,回收时将Eden空间和surivivor空间的可用对象拷贝到另一个surivivor空间中,再清理原空间。

(3)标记--整理算法。与标记--清除算法类似,只是清理后,会将可用对象都向一端移动,形成连续的内存空间。

(4)分代收集算法。是前面几种算法的综合,将内存空间分为几块,有新生代和老年代,当新生代对象销毁较多

时就可以采用复制算法处理,老年代对象存活率比较高则可以用标记--整理或标记--清除算法。

4、内存分配和回收策略。

(1)优先分配Eden空间。

(2)大对象直接进入老年代。新生代采用的复制算法,对大对象的操作消耗比较大。

(3)长期存活的对象直接进入老年代。

原创粉丝点击