JVM-GC

<尚春艳>整理

首先在理论上了解两个算法和四个引用

引用计数算法:给每个对象添加引用计数器,每当有外部引用的时候就将该值增加1,引用去掉(失效)时,该值就减1.当某个对象的计数器为0,则说明这个对象以死,可以被垃圾回收器回收掉。
这种方法的实现比较简单,也比较高效,但是一个缺点是不能解决循环引用的问题。现在的虚拟机中并没有采用这个算法来判断一个对象是否还存活着。

根搜索算法:从一系列被称做GC Roots的对象开始遍历,若某个对象没有被遍历到,则将这个对象判断为可回收对象。那么什么样的对象是GC Roots呢。下面是jvm的定义:
a、 虚拟机栈中帧的本地变量表中引用的对象
b、方法区中类静态属性引用的对象。
c、本地方法栈中native方法引用的对象

强引用:强引用比较好理解,比如TestObject to = new TestObject();其中to持有对这个TestObject对象的强引用。这个new出来的TestObject对象在堆上被持有强引用,垃圾回收线程不能对它进行回收,即使要抛出oom.
    
软引用:Java 2 平台中引入了 java.lang.ref 包(其中包含 SoftReference 、 WeakReference 和 PhantomReference 类)。这些类的作用是什么呢,就是在对象可以被垃圾回收的情况下还可以对它进行引用。软引用(soft reference)、弱引用(weak reference)和虚引用(phantom reference)对象提供了三种不同的方式来在不妨碍收集的情况下引用堆对象。软引用用于cache,在内存抛出OOM之前清除被软引用的对象

弱引用:弱引用不会影响垃圾回收情况。一个典型用途就是规范化映射(canonicalized mapping)。另外,对于那些生存期相对较长而且重新创建的开销也不高的对象来说,弱引用也比较有用。关键之处在于,垃圾收集器运行时如果碰到了弱可及对象,将释放 WeakReference 引用的对象。然而,请注意,垃圾收集器可能要运行多次才能找到并释放弱可及对象。

虚引用:只能用于跟踪对被引用对象即将进行的收集。每个虚引用都要用一个ReferenceQueue相对应。需要 ReferenceQueue 是因为它能够充当通知机制。当垃圾收集器确定了某个对象是虚可及对象时, PhantomReference 对象就被放在它的 ReferenceQueue 上。将 PhantomReference 对象放在 ReferenceQueue 上也就是一个通知,表明 PhantomReference 对象引用的对象已经结束,可供收集了。这使您能够刚好在对象占用的内存被回收之前采取行动。

具体的类图和例子

JVM-GC_第1张图片

public class TestReference {
 
    public static void main(String[] args) {
        String abc = new String("abc");
        ReferenceQueue rq = new ReferenceQueue();
        WeakReference<String> wr = new WeakReference<String>(abc,rq);
        abc=null;
        System.gc();
        System.gc();
 
    }
}
此时对象布局和执行了垃圾回收后对象布局

JVM-GC_第2张图片JVM-GC_第3张图片

几种垃圾收集算法简述

标记-清除算法:首先通过前面的根搜索算法找到需要垃圾回收的对象,然后将这些对象回收掉。存在是效率和空间碎片的问题

复制算法:为了解决效率问题,将空间分分成两块,当其中一块用完时,将扔存活的对象复制到另一块内存上。缺点是内存减少了一半。现在通常的作法是:将内存分为一个大的Elen空间和两个较小的Survivor空间,每次使用Elen和其中一块Surivivor空间。当回收时,将存活的复制到另一个Surivivor空间中。使用这种算法需要另一块内存充当分配担保,因为ELEN+Surivivor>>Surivivor

标记整理算法:老年代使用的算法,因为老年代存活率教高,使用复制算法效率不合适。该算法首先使用标记-清除算法,后面再将存活的对象移动到一起。

分代收集算法:当前商业虚拟机的垃圾收集都采用这种算法,根据对象的存活周期的不同将内存划为几块。一般将java堆分成新生代和老年代,在不同的年代采用不同的垃圾收集算法。

年轻代:先在Elen区中创建对象,当Elen区满时有一次gc过程,后将存活的对象放在一个survivor中。当这个survivor满时,gc后此区存活的对象将被放到另一个survivor中。当这个survivor也满时,通过gc后从第一个survivor的仍存活的对象将被复制到老年代中。

finalize原理:当垃圾收集器准备好回收对象所占用的内存空间(而不是对象)时,jvm会先调用这个方法,而且只有在下次垃圾回收过程中才会真正将这个对象的内存回收。可以在这个期间finalize做一些重要的清除工作。

几种垃圾收集器简述

新生代的收集器:Serial, ParNew, Parallel Scavenge分别是单线程,多线程和以吞吐量(运行用户代码时间/(运行用户代码时间+垃圾收集时间))为目标的收集器
老年代的收集器:Serial Old,Parallel Old,CMS分别是单线程,以吞吐量为目标和以获取最短回收停顿时间为目标的收集器
每种收集器都有它的优势和缺点,需根据具体的应用场景和服务器配置选择不同的组合。可以通过jvm提供的参数选择不同的组合

JVM-GC_第4张图片

通过几个小例子看下几个分区的变化情况。在debug参数中设置-XX:+PrintGCDetails可以打印出堆的使用情况

public class TestGc {
 
    private static final int mb = 1024*1024;
 
    public static void main(String[] args) {
        byte[] allocation2  = new byte[12* mb];
 
    }
 
}
执行后堆的变化为:
Heap
 PSYoungGen      total 36736K, used 14807K [0x00000007d6f60000, 0x00000007d9860000, 0x0000000800000000)
  eden space 31488K, 47% used [0x00000007d6f60000,0x00000007d7dd5da0,0x00000007d8e20000)
  from space 5248K, 0% used [0x00000007d9340000,0x00000007d9340000,0x00000007d9860000)
  to   space 5248K, 0% used [0x00000007d8e20000,0x00000007d8e20000,0x00000007d9340000)
 PSOldGen        total 84032K, used 0K [0x0000000784e00000, 0x000000078a010000, 0x00000007d6f60000)
  object space 84032K, 0% used [0x0000000784e00000,0x0000000784e00000,0x000000078a010000)
 PSPermGen       total 21248K, used 3162K [0x000000077fc00000, 0x00000007810c0000, 0x0000000784e00000)
  object space 21248K, 14% used [0x000000077fc00000,0x000000077ff168f0,0x00000007810c0000)
eden区域使用了47%
ublic class TestGc {
 
    private static final int mb = 1024*1024;
 
    public static void main(String[] args) {
        byte[] allocation1  = new byte[20* mb];
        byte[] allocation2  = new byte[5* mb];
        allocation1 = null;
        byte[] allocation3  = new byte[5* mb];
    }
 
}
[GC [PSYoungGen: 27489K->511K(36736K)] 27489K->5631K(120768K), 0.0060850 secs] [Times: user=0.01 sys=0.01, real=0.00 secs]
Heap
 PSYoungGen      total 36736K, used 6891K [0x00000007d6f60000, 0x00000007db720000, 0x0000000800000000)
  eden space 31488K, 20% used [0x00000007d6f60000,0x00000007d759aef8,0x00000007d8e20000)
  from space 5248K, 9% used [0x00000007d8e20000,0x00000007d8e9fe18,0x00000007d9340000)
  to   space 5248K, 0% used [0x00000007db200000,0x00000007db200000,0x00000007db720000)
 PSOldGen        total 84032K, used 5120K [0x0000000784e00000, 0x000000078a010000, 0x00000007d6f60000)
  object space 84032K, 6% used [0x0000000784e00000,0x0000000785300010,0x000000078a010000)
 PSPermGen       total 21248K, used 3171K [0x000000077fc00000, 0x00000007810c0000, 0x0000000784e00000)
  object space 21248K, 14% used [0x000000077fc00000,0x000000077ff18f48,0x00000007810c0000)
若创建的是大对象,则直接创建在老年代中

你可能感兴趣的:(jvm,GC,垃圾收集)