明确垃圾回收器组合
-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的查看方法
- java -XX:+PrintCommandLineFlags -version
- 通过GC的日志来分辨
Linux下1.8版本默认的垃圾回收器到底是什么?
1.8.0_181 默认(看不出来)Copy MarkCompact
1.8.0_222 默认 PS + PO
面对如此众多的垃圾回收器,我们应该如何选择一款适合自己应用的收集器呢?这个问题的答案主要受以下三个因素影响:
-
应用程序的主要关注点是什么?
a. 数据分析、科学计算类的任务,目标是能尽快算出结果,那吞吐量就是主要关注点;
b. SLA 应用,那停顿时间直接影响服务质量,严重的甚至会导致事务超时,这样延迟就是主要关注点;
c. 客户端应用或者嵌入式应用,那垃圾收集的内存占用则是不可忽视的。 -
运行应用的基础设施如何?
譬如硬件规格,要涉及的系统架构是 x86-32/64、SPARC 还是ARM/Aarch64;处理器的数量多少,分配内存的大小;选择的操作系统是 Linux、Solaris 还是 Windows等。 -
使用 JDK 的发行商是什么?
版本号是多少?是 ZingJDK/Zulu、OracleJDK、Open-JDK、OpenJ9 抑或是其他公司的发行版?该 JDK 对应了《Java 虚拟机规范》的哪个版本?
CMS存在的问题
-
CMS收集器对处理器资源非常敏感
CMS的在垃圾清除是使用并发清除的,如果处理器核数不高的情况下,垃圾回收会造成很高的负载。 -
并发回收造成的内存不足
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收集器来重新进行老年代的垃圾收集。(这样的话时间就会变得很长)
- 内存碎片问题
由于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。有三种:
- Small:就是一个2MB的region。
- Medium:32mb。2MB*16。
- Large:N*2MB。
网友评论