Java -GC 垃圾回收器

GC 垃圾回收器:

   

    简介:GC 垃圾回收器是 JVM 中自动内存管理机制的具体实现,在 HotSpot 虚拟机中 GC 的工作主要划分为两
大类,分别是内存动态分配和垃圾回收,在内存执行分配之前,GC 首先会对内存空间进行划分,考虑到 JVM
中存活对象的生命周期会具有两极化,应该采取不同的垃圾收集策略,分代收集可以实现这个目标,目前几
乎所有的GC 都是采用分代收集算法执行垃圾回收。

         一般来说当内存空间中的内存消耗到达一定阈值之后,GC 就会执行垃圾回收,而且回收算法必须非常准确,
一定不能造成内存中存活的对象被错误的回收掉,也不能造成已经死亡的对象没有及时回收,而且 GC 执行
内存回收的时候应该做到高效,不应该导致程序长时间的暂停,以及要避免产生内存碎片,不过 GC 回收垃
圾的时候不可避免的会产生碎片,因为被回收的对象空间不是连续的,这样一来会导致没有足够的空间分
配给大内存对象,不过可以通过压缩算法来消除碎片。

 

  评估一个 GC 的性能:

              1. 吞吐量:程序的运行时间(程序时间+回收时间)
             

              2. 垃圾回收开销: 吞吐量的补数,垃圾回收器所占时间与总时间的比例
             

              3. 暂停时间: 执行垃圾回收的时候,程序的工作线程被暂停的时间

              4. 收集频率: 相对于程序的执行,收集操作发生的频率

              5. 堆空间: Java 堆占用的空间大小

              6. 快速: 一个对象从创建到被回收所经历的时间

 

常见垃圾回收器:

  1.  Serial 收集器 
  2.  ParNew 收集器 
  3.  Parallel 收集器
  4.  CMS 收集器
  5.  G1回收器

一、 Serial 收集器 

         Serial收集器是JAVA虚拟机中最基本、历史最悠久的收集器,在JDK 1.3.1之前是JAVA虚拟机 新生代 收集
的唯一选择。Serial收集器是一个串行垃圾收集器,但它的“单线程”的意义并不仅仅是说明它只会使用一
个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工
作线程,直到它收集结束,通过 -XX:+UseSerialGC 指定使用 Sarial 收集器。
        Serial收集器到JDK1.7为止,它依然是JAVA虚拟机运行在Client模式下的 默认新生代收集器 。它也有着优
于其他收集器的地方:简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial
收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。在用户的桌面
应用场景中,分配给虚拟机管理的内存一般来说不会很大,收集几十兆甚至一两百兆的新生代(仅仅是
新生代使用的内存,桌面应用基本上不会再大了),停顿时间完全可以控制在几十毫秒最多一百多毫秒
以内,只要不是频繁发生,这点停顿是可以接受的。所以,Serial收集器对于运行在低硬件资源的应用
来说是一个很好的选择。

   Seiral Old 收集器
除了年轻代之外,Seiral 还提供了用于执行年老代垃圾收集的 Seiral Old 收集器, Seiral Old 收集器同样也采
用了串行回收和 STW 机制,只不过回收算法为标记-压缩算法,在 JVM 受制于单个 CPU 核心的环境下,使用
Seiral 收集器和 Seiral Old 收集器组合执行 Client 模式下单内存回收将会是不错的选择,基于串行回收的
收集器适用于大多数对于暂停时间要求不高的 Client 模式下的 JVM,由于 Seiral 收集器是一个串行的独占
式的收集器,所以在堆空间比较大的应用程序中,一旦老年代的串行收集器启动,应用程序很可能会卡顿几
秒甚至更长时间。

二、ParNew 收集器

     ParNew收集器是JAVA虚拟机中垃圾收集器的一种。它是Serial收集器的多线程版本,除了使用多条线程
进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数(例如:-XX:SurvivorRatio、-
XX:PretenureSizeThreshold、-XX:HandlePromotionFailure等)、收集算法、Stop The World、对象分配规
则、回收策略等都与Serial收集器一致,通过使用 -XX:+UseParNewGC 来指定使用 ParNew 收集器
需要注意的是,ParNew在单核甚至双核环境下绝对不会有比Serial收集器更好的效果,但是随着CPU数
量的增加ParNew相较于Serial的优势会越来越明显,但并不是成倍增长的,原因还是那个,多线程切换
的开销
在Server模式下,ParNew收集器是一个非常重要的收集器,因为除了Serial收集器外,目前只有它能与
CMS收集器配合工作;但在单个CPU环境中,不会比Serail收集器有更好的效果,因为存在线程交互开销

        为什么只有ParNew能与CMS收集器配合:
CMS是HotSpot在JDK1.5推出的第一款真正意义上的并发(Concurrent)收集器,第一次实现了让垃圾收
集线程与用户线程(基本上)同时工作;
CMS作为老年代收集器,但却无法与JDK1.4已经存在的新生代收集器Parallel Scavenge配合工作;这是因
为Parallel Scavenge(以及G1)都没有使用传统的GC收集器代码框架,而另外独立实现,而其余几种收
集器则共用了部分的框架代码。

三、Parallel 收集器

          首先第一点,也是针对Eden内存回收,它是从java 5开始出现,也是多线程(并行)GC收集器,采用的也是
跟ParNew一样的,标记复制算法;唯一的区别在于Parallar scavenge更注重吞吐量,通过相关JVM配置
可以让JVM调整Heap相关区域的内存大小,从而达到能调整吞吐量的效果;因此它也被称为吞吐量优先
的垃圾收集器,另外,它还有个悲观策略,因为该策略,有时候看到Old还有剩余空间却发生了Full GC,可
以通过 -XX:+UseParallelGC 来开启新生代Parallel收集器。
        需要注意点事,垃圾收集器中的吞吐量和低延迟这两个目标是相互矛盾的,如果要选择吞吐量,一定需要降
低垃圾收集的频率,但是这样又会导致在 GC 的时候需要更长时间来回收,反之为了保证低延迟,降低回收
的暂停时间,就必须频繁回收对象,这样又引起了年轻代内存的缩减和程序的吞吐量下降
同样,Parallel 收集器也提供了用于执行老年代垃圾收集的Parallel Old 收集器,采用的是标记-压缩算法,也
是基于并行收集和 STW,在吞吐量优先的场景,使用Parallel 收集器和Parallel Old收集器 组合,在 Server 模
式下性能不错,通过-XX:+UseParallel OldGC 开启年老代和年轻代都使用Parallel 收集器。

四、CMS 收集器

1. CMS(Concurrent Mark Sweep)收集器是一种获取最短回收停顿时间为目标的收集器,工作在老年代。

2. CMS收集器是一种基于"标记-清除"算法实现的收集器,整个过程分为四步
              初始标记(CMS initial mark).该过程分为两步:
              标记GC Roots可达的老年代对象
              遍历新生代对象,标记可达的老年代对象
              并发标记(CMS concurrent mark)
              重新标记 (CMS remark)
              并发清除 (CMS concurrent sweep)

 1. CMS 执行生命周期以一个称为 初始标记阶段 开始,在这个阶段程序所有的工作线程都会因为 STW
      而暂停,这个阶段的主要任务就是标记内存中那些被根对象集合所链接的对象是否可达,一旦标记完
      成就会恢复被暂停的工作线程。

 2. 并发标记阶段, 这个阶段仍然会触发 STW,这个阶段的主要任务是将之前不可达的对象标记为垃圾
    对象,在执行最后回收之前,尽管这些看起来是垃圾对象,但是由于在并发标记阶段,程序中的工作线程
    和垃圾收集线程交替执行,因此在并发阶段无法保证被标记为垃圾的对象引用关系遭到破坏,为了解
    决这个问题 ,CMS 会再次进入标记阶段,这样程序会因为 STW 再次在暂停。

 3. 重新标记阶段 ,对之前的对象进行再次标记,用于检查状态是否被改变。

 4. 并发清除 清除无用(被标记的)对象。此阶段的回收是并发性的,非独占式的。

CMS优点:
(1)并发收集
(2)尽可能降低停顿
       真的是一个很优秀的垃圾收集器!

CMS 收集器的缺点:

(1)CMS收集器对CPU非常敏感 。

(2)CMS收集器无法处理浮动垃圾 。

(3)会产生大量的内存碎片 。

五、 G1回收器

      G1 Garbage First, JDK7U4版本时候推出的新的垃圾收集器,G1采取了与之前的三个完全不同方式,从
而解决了前面三个的很多缺陷,从长期目标来说,G1是为了取代 CMS 存在的,G1引入了 Region 区域概
念,从而避免了 GC 操作需要在整个 Java 堆或者整个年代进行,从而节省大量时间,提高吞吐量,降低
延迟。

      G1是一个压缩式的并行独占分代收集器,和其他的收集器一样,当一个年轻代收集执行时,整个年轻代
会被回收,所有的应用线程都会被暂停,G1会启动多线程进行年轻代回收,和年轻代不一样.老年代的
G1收集器和其他的收集器不一样,G1的老年代不需要整个老年代被回收,一次只需要扫描/回收一小
部分的老年代 Region 就可以了,此外,需要注意的是这个老年代 Region 是和年轻代一起回收的,和
CMS 类型,当老年代空间耗尽的时候,G1 GC 回启动一个失败保护的应急机制(FULL GC),改机制会收集
压缩整个老年代。

特点:

         从分代上来说,G1仍然是区分年轻代和老年代,并且年轻代还是分为 Eden 去和 Survivor 区,但是从堆
结构上来说,它并不要求整个 Eden 区,年轻代或者老年代包含的 Region 是物理连接的,G1使用了全新
的分区算法.特点如下:

1. 并行性: 在回收期间,可以有多个 GC 线程同时工作,可以有效的利用多核的计算能力。

 2. 并发行: G1拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行,因此不会在整
个回收阶段发生完全阻塞应用的的情况。

3. 分代 GC: G1依然是一个分代 收集器,和其他的收集器不同,它同时兼顾了年轻代和老年代,而其
他收集器,或者工作在年轻代或者工作在老年代。

4. 空间整理: G1在回收过程中会对对象进行适当的移动,不像 CMS 那样只是简单的标记清理对
象,在若干次回收后,CMS 必须进行一次内存清理,而 G1不同,他每次回收都会有效的复制对象,减
少空间碎片,进而提高内部循环速度。

5. 可预见性: 由于分区的原因,G1可以只选取部分区域进行内存h回收,这样缩小了回收的范围,因
此对于全局停顿情况的发生也能得到较好的控制。
        随着 G1的出现,GC 从传统的连续堆内存布局主键走向了不连续内存块布局,这是通过引入 Region 概念
实现的,也就是说由一堆不连续的 Region 组成了堆内存,当然并不是真的不连续,只是由原先的物理连续
主键转变为逻辑连续,通过 Region 动态分配可以把一个 Region 分配给 Eden 或者 Survivor 或者老
年代或者大对象区间或者空闲区间等任意一个。

 

 在JDK1.9中移除了JDK 8中已弃用的垃圾收集器(GC)组合。

在JDK1.9以后,将不再推荐使用CMS垃圾回收器,官方推荐使用 G1 垃圾回收器。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值