垃圾收集器与内存分配策略

GC需要考虑什么?

  1. 哪些内存需要回收?
  2. 什么时候回收?
  3. 如何回收?

为什么学习GC?

  • 排查内存溢出
  • 排查内存泄露
  • 性能调优,排查并发瓶颈

对象存活判断?

1. 引用计数算法(Reference Counting)

给对象添加一个引用计数器,每当有一个地方引用它,计数器就加1,当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用。

问题:主流Java虚拟机没有选用引用计数算法来管理内存,主要原因是它很难解决对象之间相互循环引用的问题。

2. 可达性分析(Reachability Analysis)

基本思想:通过一系列称为“GC root”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC root没有任何引用链相连(图论里即不可达)时,则证明此对象是不可用的。

GC root包括虚拟机栈中(栈帧中的本地变量表)引用的对象;方法区中类静态属性引用的对象;方法区中常量引用的对象;本地方法栈中JNI(即一般说的native方法)引用的对象。

java 有问题时自动dump_java

引用类型

无论是引用计数算法还是可达性分析算法判断对象是否存活,都与“引用”有关。Java1.2后对引用概念进行了扩充,将引用分为:

1. 强引用(Strong Reference):只要强引用还在,垃圾收集器永远不会回收掉被引用的对象
2. 软引用(Soft Reference):用来描述一些还有用但并非必须的对象。jdk1.2提供SoftReferenc类实现软引用
3. 弱引用(Weak Reference):也用来描述非必需对象,但是强度比软引用更弱。被弱引用的对象只能生存到下一次垃圾收集发生之前。类WeakReference实现弱引用
4. 虚引用(Phantom Reference):也称为幽灵引用幻影引用。最弱的一种引用关系。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。类Phantom实现虚引用。

**

在可达性分析算法中,真正宣告一个对象死亡,至少需要经历两次标记过程:

如果对象在进行可达性分析后发现没有与GC root相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”,此时对象宣告死亡。

垃圾回收器准备释放内存的时候,会先调用finalize()。任何一个对象的finalize()方法都只会被系统自动调用一次

/**
 * @author xianggen
 * @date 2016年7月26日 上午11:52:00
 */
package xianggen.gc;


public class FinalizeEscapeGC{
    public static FinalizeEscapeGC SAVE_HOOK=null;
    public void isAlive(){
        System.out.println("yes,still alive!");
    }
    /**
     * finalize 方法
     */
    protected void finalize() throws Throwable{
        super.finalize();
        System.out.println("finalize method executed");
        FinalizeEscapeGC.SAVE_HOOK=this;   //通过引用一次当前对象,使之逃脱被收回命运
    }
    public static void main(String[] args) throws Throwable {
        SAVE_HOOK=new FinalizeEscapeGC();

        //对象第一次通过finalize方法拯救自己
        SAVE_HOOK=null;
        System.gc();
        Thread.sleep(500);//因为finalize方法优先级很低,所以暂停0.5秒等待它
        if(SAVE_HOOK!=null){
            SAVE_HOOK.isAlive();
        }else{
            System.out.println("SAVE_HOOK is dead!");
        }

        //与上面代码完全一样,但是finalize已经调用过一次!这次无法对象自救
        SAVE_HOOK=null;
        System.gc();
        Thread.sleep(500);//因为finalize方法优先级很低,所以暂停0.5秒等待它
        if(SAVE_HOOK!=null){
            SAVE_HOOK.isAlive();
        }else{
            System.out.println("SAVE_HOOK is dead!");
        }
    }
}

回收方法区

一般来说,方法区(HotSpot虚拟机中的永久代)几乎没有垃圾收集的,其性价比一般较低。

永久代的垃圾收集主要回收2部分内容:废弃常量无用的类(比较苛刻)