Java技术升华面试精选

如何选择JVM垃圾回收器?

2021-05-30  本文已影响0人  林_fbb8

明确垃圾回收器组合

-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

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

默认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核心亮点就是:动态

动态表现为:

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

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

猜你喜欢

热点阅读