【JVM专题】垃圾回收算法与垃圾收集器

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/JSUTDOIT/article/details/81089422

一.垃 圾 回 收

  主要解决3个问题:

      1.如何判定对象为垃圾对象

           引用计数法   可达性分析法

      2.如何回收

           回收策略: 标记-清除算法    复制算法    标记-整理算法   分代收集算法           

           垃圾回收器: Serial   Parnew    Cms    G1

      3.何时回收

  (1)引用计数法:

  

   所以当计数值为0的时候,垃圾回收器便开始收集。但通常情况下都不使用该种策略,因器其并不能完全回

   收,例如,在堆内存中若存在对象之间的引用,便不能被回收。

扫描二维码关注公众号,回复: 2919092 查看本文章

 

 此时,若切断引用。在堆中的整体由于被对方引用,所以不会被回收,此时垃圾回收效率低。

eg:
 

  public class testGC {

      private Object instance;

    

      public testGC() {

        

          byte[] b = new byte[20*1024*1024];

    

      }

    
    public static void main(String[] args) {

        

        testGC t1 = new testGC();

        testGC t2 = new testGC();
       

        t1.instance = t2;

        t2.instance = t1;
      

        t1 = null;

        t2 = null;

        System.gc();

    }   

}

配置JVM参数

打印得到的日志

[GC (System.gc()) [PSYoungGen: 22446K->648K(37888K)] 42926K->21136K(123904K), 0.0019986 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

[Full GC (System.gc()) [PSYoungGen: 648K->0K(37888K)] [ParOldGen: 20488K->546K(86016K)] 21136K->546K(123904K), [Metaspace: 2748K->2748K(1056768K)], 0.0112990 secs] [Times: user=0.00 sys=0.02, real=0.01 secs]

Heap

 PSYoungGen      total 37888K, used 328K [0x00000000d6600000, 0x00000000d9000000, 0x0000000100000000)

  eden space 32768K, 1% used [0x00000000d6600000,0x00000000d6652030,0x00000000d8600000)

  from space 5120K, 0% used [0x00000000d8600000,0x00000000d8600000,0x00000000d8b00000)

  to   space 5120K, 0% used [0x00000000d8b00000,0x00000000d8b00000,0x00000000d9000000)

 ParOldGen       total 86016K, used 546K [0x0000000083200000, 0x0000000088600000, 0x00000000d6600000)

  object space 86016K, 0% used [0x0000000083200000,0x00000000832888d0,0x0000000088600000)

 Metaspace       used 2755K, capacity 4486K, committed 4864K, reserved 1056768K

  class space    used 295K, capacity 386K, committed 512K, reserved 1048576K

(2)可达性分析法

 通过定义GCRoot来进行从栈内存到堆中寻找,能被找到的说明是活着的对象,不能被找到的说明是垃圾对象,就要被回收。

   模型图:

如果切断了引用,则在堆中的整个一团,都将被当醉垃圾被回收。

(3)标记清除算法

   标记清除算法是其他算法的基础,但其本身有两个要解决的问题:效率问题  空间问题

  

    图中橙色表示正在被使用的空间,黄色表示可被回收的空间 。这样回收之后会造成空间的不连续,

    内存分配的时候效率就很低。又会进行多次垃圾回收。

(4)复制算法(主要在新生代)

     

      解决标记清除的效率问题

     

  

下面描述一下大概过程:模拟两个独立的区域,在上面的内存中,被标记回收的对象,清除以后。未被使用的对象,

将被连续的安排在下面的区域中,这样,当划分对象的时候,其效率大大提高。同样的,在下面的区域,被标记清除

之后,未被使用的内存也将被移动到上半部分,再被使用。

详细说明:

      内存的大概分布如下  在Eden区,只要创建新的对象,就会被扔到这个区域内。 两块Survivor为上图中

      的两块区域。Tenured Gen为内存担保,防止内存不够,作为缓冲。

   

     内存变化大致如下图,其被浪费的空间也仅占10%左右,这是一个可以被考虑的范围。

(5)标记整理算法(适用于老年代)

        

     对于老年代部分的内存,回收效率较低,会后垃圾较少的情况下,采用该算法最合适。

     

    标记整理算法可理解为    标记--整理--清楚三个部分,在上图中被标记的算法向右移动,

    右边的未被标记的算法也需要往左边移动,这样右边便会被整体清除回收,仅仅只是移动。

(6)分代收集算法

        分代收集算法并不是一个行的算法。他是有复制算法和标记整理算法的组合而实现的,

     当处于新生代,则采用复制算法。当处于老年代则采用标记整理算法。分代收集算法

     会根据不同的情况采用不同的策略。

(7)Serial垃圾收集器

       关于其特性:

    

     单线程执行模式大致如下:

    

    

        首先理解一下,单线程垃圾收集器,可能很多人会理解,单线程效率不是非常低,于是很多人就受不了

     其实不然,比如你在收集垃圾的时候又在创建垃圾,这样你就会永远无法收集完垃圾。如上图,在其他

     线程执行过程中,暂停其他线程,单独开启垃圾回收线程,当垃圾回收完毕,则可以进行其他线程,这

     样来回往复的进行垃圾收集与回收。但缺点就是,在高并发情况下,其执行效率较低。

(8)Parnew垃圾收集器

      该收集器为多线程执行模式

      使用复制算法收集(新生代)

     

   根据图可看出,与Serial垃圾收集器的不同是,在收集线程执行的过程中是多个垃圾收集线程执行,

在高并发的情况下执行效率较高,降低垃圾收集的时间,提高用户体验感。其实在某些情况下,

例如桌面客服端下,不如Serial执行效率高。

   要注意的一个点是,通常在使用Cms收集器对老年代进行收集的时候,不能同时使用Parallel进行收集,所以通

常情况下与parnew收集器进行组合使用。

(9)Parallel垃圾收集器

      

      parallel与parnew的大致功能相同,但他们的不同点在于关注点不同,即使用场景不同。

      

    parallel可进行参数的改变进行控制吞吐量:

     

    

    垃圾回收器的最大停顿时间以毫秒的单位,若时间过短,则其所能收集的内存也变小,

    但其收集的频率将变大。所以相当于一杆秤,根据应用场景不同要权衡好时间。

         

小结:

   parnew 适用于例如和用户交互的场景,垃圾收集器停顿时间短,用户体验感好。

   parallel适用于服务端开发,在高并发的情境下

猜你喜欢

转载自blog.csdn.net/JSUTDOIT/article/details/81089422