JVM--GC垃圾回收器

来源:互联网 发布:webservice接收json 编辑:程序博客网 时间:2024/05/17 08:18

概述: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)长期存活的对象直接进入老年代。