美文网首页Java技术升华面试精选
如何选择JVM垃圾回收器?

如何选择JVM垃圾回收器?

作者: 林_fbb8 | 来源:发表于2021-05-30 15:59 被阅读0次

    明确垃圾回收器组合

    -XX:+UseSerialGC 年轻代和老年代都用串行收集器
    -XX:+UseParNewGC 年轻代使用ParNew,老年代使用 Serial Old
    -XX:+UseParallelGC 年轻代使用Paraller Scavenge,老年代使用Serial Old
    -XX:+UseParallelOldGC 新生代Paraller Scavenge,老年代使用Paraller Old
    -XX:+UseConcMarkSweepGC,表示年轻代使用ParNew,老年代的用CMS + Serial Old
    -XX:+UseG1GC 使用G1垃圾回收器
    -XX:+UseZGC 使用ZGC垃圾回收器

    垃圾收集器跟内存大小的大致关系(不精确,需要根据实际环境测试)

    Serial:几十兆
    PS:上百兆 ~ 4G
    CMS:4G ~ 10G
    G1:10G~上百G
    ZGC: 4T - 16T(JDK13)

    常见的垃圾回收器

    JDK诞生 Serial追随 提高效率,诞生了PS,为了配合CMS,诞生了PN,CMS是1.4版本后期引入,CMS是里程碑式的GC,它开启了并发回收的过程,但是CMS毛病较多,因此目前任何一个JDK版本默认是CMS 并发垃圾回收是因为无法忍受STW

    • Serial 年轻代 串行回收
    • PS 年轻代 并行回收
    • ParNew 年轻代 配合CMS的并行回收
    • SerialOld
    • ParallelOld
    • ConcurrentMarkSweep 老年代并发的,垃圾回收和应用程序同时运行,降低STW(Stop the World)的时间(200ms)。CMS问题比较多,所以现在没有一个版本默认是CMS,只能手工指定 CMS既然是MarkSweep,就一定会有碎片化的问题,碎片到达一定程度,CMS的老年代分配对象分配不下的时候,使用SerialOld 进行老年代回收。
    • G1(10ms) 算法:三色标记 + SATB
    • ZGC (1ms) PK C++ 算法:ColoredPointers + LoadBarrier
    • Shenandoah 算法:ColoredPointers + WriteBarrier
    • Eplison

    常见垃圾回收器组合参数设定:(1.8)

    • -XX:+UseSerialGC = Serial New (DefNew) + Serial Old
      小型程序。默认情况下不会是这种选项,HotSpot会根据计算及配置和JDK版本自动选择收集器
    • -XX:+UseParNewGC = ParNew + SerialOld这个组合已经很少用(在某些版本中已经废弃)
    • -XX:+UseConc(urrent)MarkSweepGC = ParNew + CMS + Serial Old
    • -XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默认) 【PS + SerialOld】
    • -XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old
    • -XX:+UseG1GC = G1

    默认GC的查看方法

    1. java -XX:+PrintCommandLineFlags -version
    2. 通过GC的日志来分辨

    Linux下1.8版本默认的垃圾回收器到底是什么?
    1.8.0_181 默认(看不出来)Copy MarkCompact
    1.8.0_222 默认 PS + PO

    面对如此众多的垃圾回收器,我们应该如何选择一款适合自己应用的收集器呢?这个问题的答案主要受以下三个因素影响:

    1. 应用程序的主要关注点是什么?
      a. 数据分析、科学计算类的任务,目标是能尽快算出结果,那吞吐量就是主要关注点;
      b. SLA 应用,那停顿时间直接影响服务质量,严重的甚至会导致事务超时,这样延迟就是主要关注点;
      c. 客户端应用或者嵌入式应用,那垃圾收集的内存占用则是不可忽视的。

    2. 运行应用的基础设施如何?
      譬如硬件规格,要涉及的系统架构是 x86-32/64、SPARC 还是ARM/Aarch64;处理器的数量多少,分配内存的大小;选择的操作系统是 Linux、Solaris 还是 Windows等。

    3. 使用 JDK 的发行商是什么?
      版本号是多少?是 ZingJDK/Zulu、OracleJDK、Open-JDK、OpenJ9 抑或是其他公司的发行版?该 JDK 对应了《Java 虚拟机规范》的哪个版本?

    CMS存在的问题

    1. CMS收集器对处理器资源非常敏感
      CMS的在垃圾清除是使用并发清除的,如果处理器核数不高的情况下,垃圾回收会造成很高的负载。

    2. 并发回收造成的内存不足
      2.1 造成原因(浮动垃圾)
      在CMS的并发标记和并发清理阶段,用户线程是还在继续运行的,程序在运行自然就还会伴随有新的垃圾对象不断产生,而这部分的垃圾对象是出现在标记过程结束以后,CMS无法在当次收集中处理掉它们,只好留在下次垃圾收集时再清理掉。这样的垃圾就叫做浮动垃圾。由于垃圾收集和用户线程是并发执行的,因此CMS收集器不能像其他收集器那样几乎填满了再进行收集,需要预留一些空间用来保存用户新创建的对象。
      2.2. 如何处理
      在JDK1.5之前老年带使用了68%空间后就会激活CMS收集。
      如果实际应用中可以适当调整参数-XX:CMSInitiatingOccu-pancyFraction 的值来提高CMS的触发百分比,降低内存回收频率获得更好的性能。
      到了JDK6 CMS收集器的启动阀值就已经默认提升到92%。
      2.3 存在问题
      如果预留空间不够怎么办?
      首先要确定这是个小概率事件,其次JVM对着的情况处理如下:

    CMS垃圾回收报错(Concurrent Model Failure) 并发失败。
    启动后备预案:冻结用户线程的执行,临时启用Serial Old收集器来重新进行老年代的垃圾收集。(这样的话时间就会变得很长)

    1. 内存碎片问题
      由于CMS老年代使用标记-清除回收策略,因此会有内存碎片问题。当碎片过多时,将会给大对象分配带来麻烦,往往会出现老年代还有很多空间但就已经不能保存对象了。不得不提前触发一次Full GC。为了解决这个问题,CMS收集器提供了-XX:UseCMSCompactAtFullCollection开关参数,用于在CMS收集器不得不进行Full GC时开启内存碎片的合并整理过程。 有参数可以配置有多少次Full GC会堆内存碎片进行整理(-XX:CMSFullGCsBeforeCompaction)

    G1

    G1打破了以往将收集范围固定在新生代或老年代的模式,GI 将 Java 堆空间分割成了若干相同大小的 区域,即 region,包括 Eden、
    Survivor、 Old、 Humongous 四种类型。其中, Humongous 是特殊的 Old 类型,专门 放置大型对象。这样的划分方式意昧着不需要一个连续的内存空间管理对象。 GI 将空间分为多个区域,优先回收垃圾最多的区域。 GI 采用的是好的空间整合能力’不会产生大量的空间碎片。

    Region的数值是在1M到32M字节之间的一个2的幂值数,JVM会尽量划分2048个左右、同等大小的Region。

    GI 的一大优势在于可预测的停顿时间, 能够尽可能快地在指定时间内完成垃圾回收任务。在 JDKl l 中,已经将 GI 设为默认 垃圾回收器。

    ZGC

    和G1类似,但ZGC的region的大小更加灵活和动态。zgc的region不会像G1那样在一开始就被划分为固定大小的region。

    zgc的region核心亮点就是:动态

    动态表现为:

    • 动态地创建和销毁。
    • 动态地决定region的大小。它的最小单位是2MB的一个块。然后每个region的大小就是是2MB*N就是。

    而且他有个概念叫:size groups。有三种:

    1. Small:就是一个2MB的region。
    2. Medium:32mb。2MB*16。
    3. Large:N*2MB。

    相关文章

      网友评论

        本文标题:如何选择JVM垃圾回收器?

        本文链接:https://www.haomeiwen.com/subject/ontmsltx.html