Java虚拟机学习笔记(二)--垃圾回收

如何判定哪些对象为垃圾对象

  • 引用计数法

实现方式:   在对象中添加一个引用计数器,当有地方引用这个对象的时候,引用计数器的值就+1,当引用失效的时候,计数器的值就-1,当计数器的值为0的时候,代表这个对象时垃圾对象,可以执行回收

优点:  效率高

缺点:  无法解决对象间循环引用的问题

举例:

/**
 * 判定哪些内存需要回收,引用计数算法
 * -verbose:gc 垃圾回收日志信息
 * -xx:PrintGCDetail 打印详细的GC信息
 */
public class ReferenceCountDemo {
    private Object instance;

    public ReferenceCountDemo() {
        byte[] m = new byte[20 * 1024 * 1024];
    }

    public static void main(String[] args) {
        ReferenceCountDemo m1 = new ReferenceCountDemo();
        ReferenceCountDemo m2 = new ReferenceCountDemo();
        m1.instance = m2;  //m1对象引用m2
        m2.instance = m1;  //m2对象引用m1
        m1 = null;  //将m1的指针清空
        m2 = null;  //将m2的指针清空
        //将以上指针清空后,由于m1对象和m2对象相互 引用,导致两个对象的计数器都是1,因此不能回收

        System.gc();

    }
}

以上例子中m1与m2相互引用,导致不能回收,但我们配置上jvm参数,看一下垃圾回收日志,发现对象被回收了,是因为,垃圾回收器采用的是可达性分析法,因此不必在意上述例子的执行结果,只要明白了是什么意思就可以了!垃圾回收日志如下:

[GC (System.gc()) [PSYoungGen: 23830K->872K(38400K)] 44310K->21360K(125952K), 0.0049954 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 872K->0K(38400K)] [ParOldGen: 20488K->770K(87552K)] 21360K->770K(125952K), [Metaspace: 3303K->3303K(1056768K)], 0.0180230 secs] [Times: user=0.00 sys=0.00, real=0.02 secs] 

  • 可达性分析法

实现方式:   通过一系列称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连,则证明此对象是不可用的,所以它们将会被判定为可回收的对象。

Java语言中的可作为GC Roots的对象包括:

  1. 虚拟机栈(局部变量表)引用的对象
  2. 方法区中类静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法栈中JNI引用的对象

举例:

Obj1及下的两个对象对是GC Root的可达对象,所以不会被当做垃圾对象回收,而Obj4及以下的两个对象不是GC Root的可达对象,所以会被当做垃圾对象回收。


垃圾回收算法

  • 标记-清除算法

       该算法的两个阶段:      ①标记  ②清除

   

    缺点:

            1.标记和清除两个过程的效率都不高

            2. 标记清除之后会产生大量的内存碎片, 空间碎片太多可能会导致,当要给一个大对象分配内存的时候,没有足够大的                  连续内存分配给该大对象,从而会提前出发一次垃圾回收,则又会消耗时间!

  • 复制算法

       是为了解决标记-清除算法的效率问题,将可用内存按容量划分为大小相等的两块。每次只使用一块,当这块用完了,就将         还存活着的对象复制到另外一块上面。然后再把已使用过的内存空间一次清理掉。


缺点:    空间浪费,将有一般的空间会浪费,当对象存活率较高时,要进行较多的复制操作,因此这种算法不适用老年代

解决空间浪费方案:

       堆细分为生存区(Survivor),伊甸园(Eden)和老年代,(Tenured Gen),当创建对象时,将对象放进伊甸园,当伊甸园内存分完之后,会放到其中的一个Survivor区,一般认为生存对象为10%,进行垃圾回收时,会将其中一个Survivor区和Eden中的存活对象复制到另一个Survivor区中,当Survivor区存不下时,就会通过分配担保进入Tenured Gen中。


回收过程如下:


  • 标记整理算法

将所有存活的对象移动到一端,将所有所有可回收的对象移动到另一端,直接清理掉端边界以外的内存


  • 分代收集算法

根据对象存活周期的不同将内存划分为几块,一般是把java堆分为新生代和老年代。这样就可以根据各个年代的特点采用适当的收集算法。

猜你喜欢

转载自blog.csdn.net/zh15732621679/article/details/80227042