You have to understand the java memory management mechanism (four) - Garbage Collection

Foreword

  In the previous three articles, the JVM memory layout, memory allocation, garbage mark made more introduction, garbage have been marked out, and that the rest is how to efficiently recycle it! This article will focus on how to recycle old mobile phones, computers, color TV, refrigerator ~ ah bah (⊙o⊙) ... will highlight the main features of several garbage collection algorithm, HotSpot commonly used garbage collector and application scenarios. At the same time, this article is the last in this series of article it!

Related Links (Note: The article to explain the Hotspot JVM virtual machine, for example, jdk version 1.8)
1,  you must understand java memory management mechanisms - the runtime data area
2,  you must understand java memory management - memory allocation
3,  you have to understand the java memory management - garbage mark
4,  you must understand java memory management - garbage Collection

text

  The previous article, we detail two marking algorithm, and reachability analysis algorithm to do more introduction. We also know that the HotSpot in the specific implementation OopMap + RememberedSet how to use technology to achieve "accurate type GC". Whether marking is accurate and efficient recycling target what optimization technology, the goal! So, for efficient garbage, virtual machine which has experienced the evolution of technology and algorithms and optimized it? (Note: G1 collector and collection algorithm article does not cover, because I think you can write a separate article later to talk about!)

  Here we will introduce several common recovery algorithm, and then learn how to select and optimize these different kinds of algorithms JVM Chinese.

  "Mark - sweep" algorithm is divided into two stages, "marking" and "clear." Mark or that mark, in the last article has done more introduced, JVM in After marking execution, was still "will recover" a collection of objects recovered will be unified. FIG performed as follows:

  

  Advantages:
    1, reachability analysis algorithm based on the most basic, it is the most basic collection algorithm.
    2, the follow-up algorithms are collected, and insufficient improvement obtained based on this idea.
  Disadvantages:
    1, the efficiency is not high.
    2, can be seen from the chart this collection algorithm will generate a lot of discontinuous memory fragmentation, if this time you need to create a large object, it can not be allocated.

  "Copy" algorithm according to the memory capacity is divided into two equal size, each one of which is used. When one runs out of memory, it will also copy objects to survive on top of another piece, and then the storage space has been used to clean out one-time, so every time to recover for the entire half memory area, regardless of debris . FIG performed as follows:

  

  Advantages:
    1, for each half of the recovery zone, simple, efficient operation.
    2, no memory fragmentation problems.
  Disadvantages:
    1, the memory will be reduced to the original general, the price is high.
    2, when the object is high survival rate, the need for more copy operation, efficiency will be low.

  "Copy modified version of the algorithm" to replace the original memory into two solutions, a large memory into a memory (referred to Eden space) and two smaller memory (referred Survivor spaces), each use Eden space and space in which a Survivor. When recycling, the Eden and Survivor which is also a one-time live objects copied to another piece of space Survivor, Eden finally clean out Survivor and just used space. FIG performed as follows:

  

  Advantages:
    1, to improve the shortcomings of ordinary replication algorithm, improved space utilization.

  "Mark - finishing" tagging process algorithms and "mark - sweep" algorithm is the same as the same, but the subsequent steps are not directly recycled objects to clean up, but to give all objects are moved to the end, then clean out directly to end border other than memory. FIG performed as follows:

  

  优点:
    1、改善了“标记-清除”算法会产生内存碎片的缺点。
    2、不会像“复制”算法那样效率随对象存活率升高而变低。
  缺点:
    1、 依然没有解决 “标记-清除”算法存在的缺点,那就是回收效率问题。还多了需要整理的过程,效率更低。

  我们都知道,在主流的虚拟机中都是采用分代收集算法来进行堆内存的回收,在第一篇文章中我们也用了一张图展示了JVM堆内存的划分。如下:

  

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

  新生代

  在Hotspot虚拟机中,新生代的收集器都是采用的改良版的复制算法进行垃圾回收。将新生代一分为三,一块Eden区和两块Survivor区。Eden区与两块Survivor区的比例为8:1:1。这样划分的依据是什么呢?基于弱代理论,IBM研究表明新生代中98%的对象都是"朝生夕死",大多数分配了内存的对象并不会存活太长时间,在处于年轻代时就会死掉。

  在原始的复制算法中,空间一分为二,空间利用率为50%,也就是说有新生代中50%的空间会被浪费,无法分配内存。Hotspot虚拟机使用改良的复制算法,并且设置合理的空间比例,新生代中可用的内存空间为整个新生代容量的90%,只有10%的空间会被浪费,大大的提高的新生代的空间利用率。如果存活对象占用的内存大于新生代容量的10%怎么办?这就需要依赖其他内存(老年代)进行分配担保了。新生代回收动图如下:

  

  老年代

  由于老年代的对象存活周期一般相对较长,不会像新生代对象那样“朝生夕死”,所以对象存活率高是老年代的特点,并且老年代也没有额外的空间可以分配担保,所以不适合采用复制算法进行回收。根据老年代的特点,一般会使用"标记-清理"或"标记-整理"算法来进行垃圾回收。

  上面我们介绍了在JVM中常用的垃圾回收算法及每一种算法的优缺点。接下里会介绍在HotSpot虚拟机中常用的几种垃圾收集器,垃圾收集器是垃圾回收算法的具体实现,不同的商家、不同版本的JVM所提供的垃圾收集器可能会存在差异。这几种收集器分别是Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1。在了解垃圾收集器之前,我们先来区分几个概念:

  并发收集器VS并行收集器
  并行:指多条收集线程同时进行收集工作,但此时用户线程处于等待状态。如ParNew、Parallel Scavenge、Parallel Old。
  并发:指用户线程与垃圾收集线程同时执行(并不一定是并行,可能会交替执行)。如CMS、G1。

  YoungGC VS OldGC VS MinorGC VS MajorGC VS FullGC
  Minor GC、YoungGC:Minor GC又称为新生代GC,所以等价于Young GC,在新生代的Eden区分配满的时候触发。在Young GC后新生代中有部分存活对象会晋升到老年代,有可能是年龄达到阈值(默认为15岁,在JVM里面15岁就步入老年生活了,O(∩_∩)O哈哈~)了,也可能是Survivor区域满了,如果是Survivor区域被填满,会将所有新生代中存活的对象移动到老年代中!

  Major GC、Old GC、Full GC:Old GC从字面能理解是老年代的GC,但是对Major GC和Full GC存在多种说法,有的认为Major GC等价于Old GC只是针对老年代的GC,有的认为Major GC和Full GC是等价的。但是我个人认为Major是指老年代GC,而Full GC针对新生代、老年代、永久代整个的回收。由于老年代的GC都会伴随一次新生代的GC,所以习惯性的把Major GC和Full GC划上了等号。前面Young GC时候说到“在Young GC后新生代中有部分存活对象会晋升到老年代”,万一老年代的空间不够存放新生代晋升的对象怎么办呢?所以当准备要触发一次Young GC时,如果发现统计数据之前Young GC的平均晋升大小比目前老年代剩余的空间大,则不会单独触发Young GC,而是转为触发Full GC,也就是整堆的收集!

  串行垃圾收集器是最基本、发展历史最悠久的收集器。主要包含Serial和Serrial Old两种收集器,分别用来收集新生代和老年代。串行收集器由于是单线程收集,在进行垃圾收集时,必须暂停(Stop The World)所有的工作线程,直到GC线程工作完成。运行示意图如下:

   

  Serial 收集器:主要针对新生代回收,采用复制算法,单线程收集。
  Serial Old收集器:主要针对老年代回收,采用“标记-整理”算法,单线程收集。

  串行收集器在单CPU的环境下,没有线程切换的开销,可以获得最高的单线程收集效率,但是由于现在普遍都是多CPU(或者多核)环境,所以除了在桌面应用中仍然将串行收集器作为默认的收集器,其他场景已经很少(很少不代表没有,后面CMS会讲到)使用。

  在上面我们谈到一个词,需要暂停(Stop The World)所有的工作线程,这个概念在后面也会多次提到,为什么需要暂停呢?一是为了方便GC动作,不然在GC过程中又会额外产生新的垃圾,或者分配新的对象。二是因为GC过程中对象的地址会发生变化,如果不暂停线程,可能会导致引用出现问题。

  并行收集器是串行收集器的多线程版本,除了多线程外,其余的行为、特点和串行收集器一样。主要包含ParNew收集器、Parallel Scavenge收集器、Parallel Old收集器。运行示意图如下:

   

  ParNew收集器:主要针对新生代回收,采用复制算法,多线程收集。一般老年代如果使用CMS收集器,则默认会使用ParNew作为新生代收集器。
  Parallel Scavenge收集器:该收集器与ParNew收集器类似,也是新生代收集器,采用复制算法,多线程收集。其他收集器关注点是尽可能地缩短垃圾收集时用户线程停顿的时间,但是Parallel Scavenge收集器的目标则是达到一个可控的吞吐量(吞吐量=CPU运行用户代码时间/(CPU运行用户代码时间+CPU垃圾收集时间)),所以该收集器也成为吞吐量收集器。由于该收集器没有使用传统的GC收集器代码框架,是另外独立实现的,所以无法和CMS收集器配合工作。
  Parallel Old收集器:主要针对老年代回收,采用“标记-整理”算法,多线程收集。该收集器是Parallel Scavenge收集器的老年代版本。在JDK1.6之后用来替代老年的Serial Old收集器。在注重吞吐量以及CPU资源敏感的场景,一般会选择Parallel Scavenge+Parallel Old的组合进行垃圾收集。

  前面介绍的几种收集器都相对比较简单,也很好理解,所以也没做过多的介绍。接下来介绍的收集器相对前面几种收集器就要复杂一些,并且使用较广,所以介绍会较详细!并发标记清理(Concurrent Mark Sweep)收集器也称为并发低停顿收集器或低延迟收集器。CMS收集器采用的是“标记-清理”算法,所以不会进行压缩操作。我们先来了解一下CMS收集器的运作过程:

  

  CMS收集器运作过程

  1、初始标记(CMS initial mark)
  仅标记GC Roots能直接关联的对象,这个阶段为速度较快,但是仍然需要“Stop The World”,但是停顿时间较短!

  2、并发标记(CMS Concurrent mark)
  进行GC Roots Tracing的过程,也就是查找GC Roots能直接关联的对象所引用的内存。在这个阶段,GC线程与用户线程是同时运行的,所以并不能保证能标记出所有存活的对象。

  3、重新标记(CMS remark)
  由于并发标记阶段,用户线程在并发运行,所以可能在并发标记阶段产生新的对象,所以在重新标记阶段也会需要“Stop The World”来标记新产生的对象,且停顿时间比初始标记时间稍长,但远比并发标记短。

  4、并发清除(CMS Concurrent sweep)
  在并发清除阶段用户线程与清理线程也是同时工作,清理线程回收所有的垃圾对象!

  CMS收集器缺点

  上面了解了CMS收集器的运作过程,不知道在了解过程中你有没有发现一些问题,比如CMS收集器采用的是“标记-清除”算法,那会不会产生很多的内存碎片?比如在并发清理阶段,用户线程还在运行,会不会在清理的过程中又产生了垃圾?总结CMS收集器的几个明显的缺点如下:

  1、 对CPU资源非常敏感
  并发收集虽然不会暂停用户线程,但是因为会占用一部分CPU资源,还是会导致应用程序变慢,总吞吐量下降。CMS的默认收集线程的数量=(CPU数量+3)/4。所以,当CPU数量大于4个时,会有超过25%的资源用于垃圾收集。当CPU数量小于或等于4个时,默认一个收集线程。

  2、 产生大量内存碎片
  CMS收集器采用“标记-清除”算法,在清除后不会进行压缩操作,这样会导致产生大量不连续的内存碎片,在分配大对象时,无法找到足够的连续内存,从而需要提前触发一次FullGC的动作。针对该问题,提供了两个参数来设置是否开启碎片整理。
  1)、“-XX:+UseCMSCompactAtFullCollection”参数
  从名字能看出来,在收集的时候是否开启压缩。这个参数默认是开启的,但是是否开启压缩还需要结合下面的参数!
  2)、“-XX:+CMSFullGCsBeforeCompaction”参数
  该参数设置执行多少次不压缩的Full GC后,来一次压缩整理。这个参数默认为0,也就是说每次都执行Full GC,不会进行压缩整理。
  如果开启了压缩,则在清理阶段需要“Stop the world”,不能进行并发!

  3、 产生浮动垃圾
  上面说到过在并发清理阶段,用户线程还在运行,这时候可能就会又有新的垃圾产生,而无法在此次GC过程中被回收,这成为浮动垃圾。

  4、 “Concurrent Mode Failure”失败
  不知道大家在开发过程中有没有遇到过“Concurrent Mode Failure”失败的信息,不管你有没有遇到过,反正我是遇到过!这个异常是什么原因导致的呢。在并发标记和并发清除阶段,用户线程与GC线程并发工作,这会导致在清理的时候又会有用户的线程在拼命的创建对象,本身垃圾回收时候肯定是可用内存不够了,可万一这时候用户线程创建了大量的对象怎么办呢?所以一般CMS收集器的垃圾回收的动作不会在完全无法分配内存的时候进行,可以通过“-XX:CMSInitiatingOccupancyFraction”参数来设置CMS预留的内存空间!如果预留的空间无法满足程序的需要,就会出现 “Concurrent Mode Failure”失败。这时候JVM会启用后备方案,也就是前面介绍过的Serial Old收集器,这样会导致另一次的Full GC的产生,这样的代价是很大的,所以CMSInitiatingOccupancyFraction这个参数设置需要根据程序合理设置!

  CMS收集器应用场景

  上面介绍了CMS收集器的缺点,那它当然也有它的优点啦,比如并发收集、低停顿等等……所以CMS收集器适合与用户交互较多的场景,注重服务的响应速度,能给用户带来较好的体验!所以我们在做WEB开发的时候,经常会使用CMS收集器作为老年代的收集器!

写在结尾

  “你必须了解的java内存管理机制”这个系列文章终于完成啦!而我也要回去过年啦!写技术文章真的挺花时间的,因为很多时候自己要去查阅很多资料,还要去做验证。所以,有时候电脑面前不知不觉就是一个周末过去了,而一篇文章还没写完……新的一年希望自己还能坚持勤记录、齐分享,督促自己去学习、去进步

Guess you like

Origin www.cnblogs.com/gogogofh/p/11119979.html