目录
什么是分代收集算法
GC的分类和专业术语
什么是垃圾收集器
垃圾收集器的分类及组合
编辑
应关注的核心指标
Serial和ParNew收集器原理
Serial收集器
ParNew收集器
Parallel和CMS收集器原理
Parallel 收集器
CMS收集器
新一代垃圾收集器G1和ZGC
G1垃圾收集器
回收原理讲解
三种垃圾回收模式
MixGC垃圾回收步骤
常见参数讲解
配置注意事项
ZGC-新一代垃圾搜集器
什么是ZGC
工作流程
平台支持说明
验证参数(JDK17环境)
关于升级建议
现在市面上充斥着许多对于JVM垃圾收集器教程,有零基础的,有专讲的 ... 这些教程或文章大都长篇大论的细扣字眼,内容繁多且没有重点,看过便会忘,本篇集结了9年老JAVA工作及面试经验,专讲面试和工作中使用到的,一站式讲解,帮助你疏通学习脉络,少走弯路。
学习本章节前,对于垃圾回收机制还不了解的话可以看看我的上篇文章进行回顾。传送门:Java高阶私房菜:JVM垃圾回收机制及算法原理探究-CSDN博客
什么是分代收集算法
针对不同生命周期的对象采用不同的垃圾回收策略,以达到更好的垃圾回收效果。年轻代空间多数对象存活时间短,可以高频地进行回收;而在老年代空间多数对象存活时间久,则需要低频回收。分代算法是根据回收对象的特点进行选择,根据上篇总结,年轻代适合标记-复制算法,老年代适合标记清除或标记压缩算法,通过将内存划分为不同的代,可以使得Minor GC的频率更高,更早地回收垃圾对象,减少Full GC的发生频率,提高整体性能。
JVM将内存分为年轻代和老年代,其中年轻代又分为Eden区和两个Survivor区。
年轻代:用于存放新生的对象,其中Eden区是新对象的分配区域,当Eden区满时,会触发Minor GC,将存活的对象移动到Survivor区,同时清空Eden区,Survivor区则用于存放经过一次Minor GC后存活的对象。
老年代:用于存放长时间存活的对象,当年轻代中的对象经过多次Minor GC后仍然存活,就会被移动到老年代,当老年代满时,会触发Full GC。
GC的分类和专业术语
Partical GC(部分收集)
新生代收集:对象从Young 区域消失(被回收)的过程称为“minor GC / Young GC”,Eden 的清理,S0\S1的清理都由于MinorGC,当YoungGen区内存不足时,就会触发minorGC。
老年代收集:对象从老年代中消失的过程,清理整合OldGen的内存空间,称为“Major GC/Old GC”,有些垃圾收集器 针对老年代单独回收,所以比较少用
Full GC(整堆收集)
清理整个堆空间,包括年轻代和老年代,可以理解为Major GC+Minor GC组合后进行的一整个过程,是清理JVM整个堆空间。
其实不用过多关心是叫Major GC还是Full GC,应该关注当前的 GC是否停止了所有应用程序的线程,许多Major GC 是由 Minor GC 触发的, 出现 Major GC通常出现至少一次的Minor GCMajorGC 的速度一般要比Minor GC慢 10倍不止。
其可手动调用System.gc( )触发, 但却不一定会立马执行Full GC,而只是提交了一个回收请求,由JVM的自动垃圾回收机制判断当前是否触发Full GC,即当老年代空间不足 , 通过Minor GC后进入老年代的平均大小大于老年代的可用内存。
需要注意的是,当执行Full GC时,它会暂停所有当前的应用线程,时间由回收效率而定,直至整个垃圾回收过程完成才恢复正常,它对系统的性能有较大影响,所以在生产环境中要尽量避免使用该方法,可以通过启动参数-XX:+ DisableExplicitGC来禁止调用System.gc()。内存的优化则尽量利用JVM垃圾回收策略来维护,包括日常的编码中不再使用的变量要置Null切断与堆空间的联系。
STW(Stop The World)
垃圾回收发生过程中,用户线程在运行至安全点(safe point)后,就自行挂起进入暂停状态,对外的表现就是卡顿,所以应尽量减少Full GC的次数,是Minor GC还是Major GC都会STW,区别只在于STW的时间长短。
JVM资料很多,有讲32位和64位的的虚拟机的参数,但是基本都不用32位操作系统(生产环境更是如此,如果仍使用32位操作系统就应该反省在这家公司的去留问题了),所以不用花时间去了解32位的。
什么是垃圾收集器
还是那句话,垃圾回收算法是内存回收的方法论,垃圾收集器则是内存回收的具体实现,目前Java规范中并没有对垃圾收集器的实现有任何规范,不同的厂商、不同的版本的虚拟机提供的垃圾收集器是不同的,主要讨论的是HotSpot虚拟机。
那么有没有最厉害的垃圾收集器呢?负责任的说其实不存在最厉害的垃圾收集器,只有在对应场景中最合适的垃圾收集器。
那为什么要有这么多的收集器呢?那是因为Java的使用场景很多,移动端,服务器等,然后内存里面对象存活时间不一样,需要针对不同的场景,提供不同的垃圾收集器,提高垃圾收集的性能。
垃圾收集器的分类及组合
新生代收集器
-
Serial 串行垃圾收集器
-
ParNew 年轻代的并行垃圾回收器
-
Parallel 并行垃圾收集器
老年代收集器
-
Serial Old 串行老年代垃圾器
-
Parallel Old 老年代的并行垃圾回收器
-
CMS (ConcMarkSweep)并发标记清除
整堆收集器:
G1:JDK9的默认垃圾收集器,以替代CMS。该收集器指出新生代和老年代不再是物理隔离的了(但还保留着新生代和老年代的概念),它们都是一部分Region (不需要连续)的集合,通过Region的动态分配方式实现逻辑上的连续。
ZGC:JDK11新引入的ZGC收集器,在该收集器中不管是物理上还是逻辑上,已经不存在新老年代的概念了,它们会被分为一个个page,当进行GC操作时会对page进行压缩,因此没有碎片问题。
那么那么多垃圾收集器,我们是否都要记住?作为新时代互联网人,还是那句话,我们只学有用的,淘汰的垃圾收集器我们不花太多时间。
垃圾收集器的组合
两个垃圾收集器之间如果存在连线,则说明它们可以搭配使用,图中:Serial old作为CMS出现"Concurrent Mode Failure"失败的后备预案
这里需要注意的是:
-
JDK8中默认使用: Parallel Scavenge GC + ParallelOld GC
-
JDK14 弃用了: Parallel Scavenge GC + Parallel OldGC
-
JDK9默认是用G1为垃圾收集器
-
JDK14 移除了 CMS GC
查看默认垃圾收集器
JVM参数: -XX:+PrintCommandLineFlags 查看命令行相关参数(包含使用的垃圾收集器)
设置默认垃圾收集器
// JDK8
-XX:InitialHeapSize=536870912 -XX:MaxHeapSize=8589934592 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
// JDK11
-XX:G1ConcRefinementThreads=9 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=536870912 -XX:MaxHeapSize=8589934592 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC
// JDK17
-XX:ConcGCThreads=2 -XX:G1ConcRefinementThreads=9 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=536870912 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=8589934592 -XX:MinHeapSize=6815736 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC
应关注的核心指标
吞吐量:运行用户代码的时间占总运行时间的比例(总运行时间 = 程序的运行时间 + 内存回收的时间),例如:虚拟机共运行100分钟,垃圾收集器花掉1分钟,那么吞吐量就是99%。
暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间,一个时间段内应用程序线程暂停,让GC线程执行的状态,GC期间100毫秒的暂停时间,说明在这100毫秒期间内没有应用程序线程是活动的。
收集频率:指垃圾回收器多长时间会运行一次。一般来说,垃圾回收器的频率应该是越低越好。
Serial和ParNew收集器原理
Serial收集器
它作为最简单的垃圾收集器,使用单线程进行垃圾收集,暂停所有应用程序线程, 在单核CPU环境来说,Serial收集器是更高效的。Serial Old是Serial收集器的老年代版本,在jdk1.5之前的版本与Parallel收集器搭配使用,或者作为CMS的备选方案。它适用于小型应用程序和客户端应用程序,一般javaweb、springboot项目不会采用这类收集器。
其实现算法视区域的不同:新生代采用复制算法,老年代采用标记整理算法
相关命令参数使用
-
同时指定年轻代和老年代都使用串行垃圾收集器
-XX:+UseSerialGC
-
查看命令行相关参数
-XX:+PrintCommandLineFlags
-
全部参数 (JDK11环境)
//参数
-XX:+UseSerialGC -XX:+PrintCommandLineFlags -Xms32m -Xmx32m
//输出
-XX:InitialHeapSize=33554432 -XX:MaxHeapSize=33554432 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseSerialGC
ParNew收集器
工作在年轻代上的,只是将串行的垃圾收集器改为了并行,其他基本和Serial一样,使用多个线程进行垃圾回收的,适用于大型应用程序和多核处理器,以及在服务端应用程序中使用,单核上效率比Serial低,和下集讲Parallel收集器类似,但Parallel收集器不兼容CMS,除了它只有Serial收集器可以和CMS收集器配合工作。
其实现算法视区域的不同:新生代采用复制算法,老年代采用标记整理算法
相关命令参数使用
-
年轻代使用ParNew回收器,老年代使用串行收集器
-XX:+UseParNewGC
-
查看命令行相关参数
-XX:+PrintCommandLineFlags
-
验证参数 (JDK8环境,如果用JDK11会报错,JDK8开始已经不再被推荐使用)
//参数
-XX:+UseParNewGC -XX:+PrintCommandLineFlags -Xms32m -Xmx32m
//输出
-XX:InitialHeapSize=33554432 -XX:MaxHeapSize=33554432 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParNewGC
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release
Parallel和CMS收集器原理
Parallel 收集器
全称 Parallel Scavenge 是一种多线程垃圾收集器,和ParNew收集器类似,是一个新生代收集器,默认线程数和cpu核数一样,用于大型应用程序和服务器应用程序,比如大批量数据处理,后台计算任务等,Parallel Old是Parallel Scavenge收集器的老年代版本,JDK8默认使用Parallel Scavenge收集器。
其实现算法视区域的不同:新生代采用复制算法,老年代采用标记整理算法
Parallel对比ParNew
-
-XX:+UseParallelGC 仅对年轻代有效,不可以和CMS收集同时使用
-
-XX:+UseParNewGC 设置年轻代为多线程收集,可以和CMS收集同时使用
相关命令参数使用
-
年轻代使用ParallelGC垃圾回收器,老年代使用串行回收器
-XX:+UseParallelGC
-
年轻代使用ParallelGC垃圾回收器,老年代使用ParallelOldGC垃圾回收器
-XX:+UseParallelOldGC
-
查看命令行相关参数
-XX:+PrintCommandLineFlags
-
验证参数(JDK11环境)
//参数一
-XX:+UseParallelGC -XX:+PrintCommandLineFlags -Xms32m -Xmx32m
//输出
-XX:InitialHeapSize=33554432 -XX:MaxHeapSize=33554432 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
//参数二
-XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+PrintCommandLineFlags -Xms32m -Xmx32m
//输出
-XX:InitialHeapSize=33554432 -XX:MaxHeapSize=33554432 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC -XX:+UseParallelOldGC
CMS收集器
全称 Concurrent Mark Sweep,是一款并发的、使用标记-清除算法的垃圾回收器,老年代中的对象生命周期较长,垃圾回收频率较低,目标是获取最短垃圾收集停顿时间,针对 老年代 垃圾的收集器,停顿时间较短,适合对响应时间要求较高的应用程序,如Web应用程序、电子商务等高并发场景。
整个过程分4步(初始标记 和 重新标记 需要stopTheWorld,并发标记与并发清除阶段不需要暂停用户线程)
-
初始标记: 标记GC Root直接关联对象,会导致stopTheWorld
-
并发标记: 与用户线程同时运行
-
重新标记:会导致stopTheWorld
-
并发清除:与用户线程同时运行
相关命令参数使用
-
年轻代使用ParNew垃圾回收器,老年代使用CMS回收器
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
-
查看命令行相关参数
-XX:+PrintCommandLineFlags
-
验证参数(JDK11环境,然后被弃用了,验证成功需要用JDK8)
//输入
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags -Xms32m -Xmx32m
//输出
-XX:InitialHeapSize=33554432 -XX:MaxHeapSize=33554432 -XX:MaxNewSize=11190272 -XX:MaxTenuringThreshold=6 -XX:NewSize=11190272 -XX:OldSize=22364160 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC
//警告信息
Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release.
上述垃圾收集器都是接近被弃用了,大体了解即可,重点掌握接下去的G1和ZGC
新一代垃圾收集器G1和ZGC
G1垃圾收集器
回收原理讲解
Garbage First 垃圾收集器是JDK7版本之后引入的一种垃圾回收器,Jdk9中将G1变成默认的垃圾收集器,可以在不同的内存区域中分配垃圾回收的工作,提高了垃圾回收效率。
它保留了分代思想,把内存划分为多个独立的区域Region,区域中包含逻辑上的年轻代、老年代区域。取消了年轻代、老年代的物理划分,不用单独对每个年代空间进行设置。Region的区域类型是动态变化的,可能之前是年轻代,经过了垃圾回收之后就变成了老年代,实现更加精细化的垃圾回收。整体采用标记整理算法, 局部是采用复制算法,不会产生内存碎片。它把整个Java堆划分成约2048个独立Region块,每个Region块大小根据堆空间的大小而定,为2的N次幂,1MB~32MB,即每个Region的大小可通过参数 -XX:G1HeapRegionSize
配置
新增加一种叫Humongous内存区域,用于存储大对象,它规定如果超过1.5个region,就是巨型对象,就放到H区,默认直接会被分配在老年代,一般被视为老年代.图中的H块,如果一个H区装不下一个巨型对象,G1会寻找连续的H区来存储,为了能找到连续的H区,有时需要启动Full GC。
相关命令参数使用
-
查看默认垃圾收集器
-XX:+PrintCommandLineFlags
查看命令行相关参数(包含使用的垃圾收集器)
//JDK11
-XX:G1ConcRefinementThreads=9 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=536870912 -XX:MaxHeapSize=8589934592 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC
三种垃圾回收模式
Young GC
G1与之前垃圾收集器的Young GC
不同,不是当新生代的Eden区放满了就进行垃圾回收,G1会计算当前Eden区回收大概需要多久时间,如果接近参数-XX:MaxGCPauseMills
设定的值,便会触发Young GC。
回收过程也是将Eden区和Survivor区中的存活对象复制到空闲的Survivor区,并清空Eden区和原来的Survivor区。如果Survivor区也满了,那么会将存活对象复制到Old区,在Young GC期间,应用程序会被暂停。
Mixed GC
多数对象晋升到老年代old region时,为了避免堆内存被耗尽问题,会触发混合的GC。回收整个Young Region,还会回收一部分的Old Region区域,注意不是全部Old Region区域。其触发条件由配置的参数 -XX:InitiatingHeapOccupancyPercent=n决定。默认为45%,即 当老年代大小占整个堆大小百分比达到该阀值时触发。
Full GC
单个线程会对整个堆的所有代中所有分区做标记、清除以及压缩动作,非常耗时。
总结
在Young GC和Mixed GC中,G1垃圾收集器都会对每个Region的存活对象数量进行统计,根据存活对象数量和空闲Region的数量,动态地决定垃圾收集的区域和顺序,这种动态的垃圾收集策略,可以避免Full GC的发生,提高了应用程序的响应速度。
MixGC垃圾回收步骤
1)初始标记(STW):记录下GC Roots能直接引用的对象,并标记所有存活的对象,会执行一次年轻代GC,需要暂停所有线程,速度很快。
2)并发标记:与应用线程一起工作,进行可达性分析,G1收集器会对堆内存进行并发标记,找出所有存活的对象,并记录它们所在的Region.
3)最终标记(STW):修正并发标记期间, 部分因程序运行导致发生变化的那一部分对象,根据算法修复一些引用的状态;
4)筛选回收(STW):对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿STW时间,即-XX:MaxGCPauseMillis
制定计划。
这里举一个成本排序案例,现在有Region1、Region2和Region3三个区域:
1)Region1预计可以回收1.5MB内存,预计耗时2MS,投产比ROI=1.5/2;
2)Region2预计可以回收1MB内存,预计耗时1MS,投产比ROI=1/1;
3)Region3预计可以回收0.5MB内存,预计耗时1MS,投产比ROI=0.5/1;
那Region1、Region2和Region3各自的回收价值与成本比值分别是:0.75、1和0.5,比值越高说明同样的付出收益越高,如果此时只能回收一个Region的内存空间,G1就会选择Region2进行回收,保证了G1收集器在有限的时间内尽可能地提高收集效率。
常见参数讲解
G1的使用方式非常的简单,只需对应开启G1垃圾收集器,配置堆最大内容及最大停顿时间便可享受到。一个搞笑的事情是在职场中,很大可能工龄十多年的老手花大半月时间去调优一次JVM参数,还不如直接升级JDK来的性能高,当然这也只是当成一个笑点,但这侧面也能看出来升级JDK的性价比,当然如果能掌握JDK,两者结合在一起加以利用能发挥它的最大效能。
相关参数
- -XX:+UseG1GC:启用G1垃圾收集器。
- -XX:G1HeapRegionSize=n:Java 堆大小划 分出约 2048 个区域,默认是堆内存的1/2000;配置需要为2的N次幂,1MB~32MB,使用G1垃圾回收器最小堆内存应为 1MB*2048=2GB ,低于这个的建议使用其它垃圾回收器。
- -XX:MaxGCPauseMillis=n:设置最大停顿时间,单位为毫秒,默认为200毫秒(JVM会尽力实现,但不能保证达到)
- -XX:ParallelGCThreads=n:设置 STW 工作线程数的值。一般设置为逻辑处理器的数量,最多为 8,是在STW阶段,并行执行【垃圾收集动作】的线程数
- -XX:ConcGCThreads=n:在【并发标记】阶段,并发执行标记的线程数,一般将 n 设置为并行垃圾回收线程数 (ParallelGCThreads) 的 1/4
- -XX:InitiatingHeapOccupancyPercent=n:设置G1 Mix垃圾回收的触发阈值,默认为45%
命令案例
//输入
-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xms524m -Xmx524m -XX:+PrintCommandLineFlags
//输出
-XX:G1ConcRefinementThreads=9 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=549453824 -XX:MaxGCPauseMillis=100 -XX:MaxHeapSize=549453824 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC
使用场景
- 大型应用程序:可以将堆内存划分为多个区域,以实现更加精细化的垃圾回收。
- 高并发、低延迟:对响应时间要求较高的应用程序,如Web应用程序、电子商务等高并发场景。
- 大内存应用:可以在垃圾回收过程中释放大量的空间,提高了内存的利用率。
配置注意事项
-
不手工设置年轻代大小,比如使用 -Xmn 选项或 -XX:NewRatio 等设置年轻代大小
-
暂停时间的目标不要太小,G1 的吞吐量目标是 90% 的应用程序时间和 10%的垃圾回收时间。如果把停顿时间调得非常低,如设置为10毫秒,很可能出现的结果就是由于停顿目标时间太短,导致每次回收内存只占堆内存很小的一部分, 收集器收集的速度跟不上分配器分配的速度, 导致垃圾慢慢堆积。应用运行时间一长就占满堆引发Full GC反而降低性能, 通常把期望停顿时间设置为一两百毫秒是比较合理的
-
避免存活时间短的大对象:G1垃圾收集器对程序的代码质量要求较高,需要对程序的内存使用情况进行精细化管理,对应用程序的代码进行优化和调整
ZGC-新一代垃圾搜集器
什么是ZGC
Z Garbage Collector 是Oracle公司开发一种可伸缩、低停顿时间的垃圾收集器,标记-复制算法(进行了改进)。垃圾回收过程几乎全部是并发,实际STW停顿时间极短,停顿时间控制10ms内,主要采用的染色指针和读屏障技术。
在 JDK 11 中是实验性的特性引入,在 JDK 15 中 ZGC 可以正式投入生产使用,使用 –XX:+UseZGC 启用,ZGC 的堆内存也是基于 Region 来分布,和G1类似,但不再区分新生代老年代的,Region 支持动态地创建和销毁,大小不是固定。
有三种类型的 Region
- 小型页面 Small Region:容量固定2MB,主要用于放置小于 256 KB 的小对象。
- 中型页面Medium Region:容量固定32MB,主要用于放置大于等于 256 KB 小于 4 MB 的对象。
- 大型页面Large Region:容量不固定 为N * 2MB, Region 是可以动态变化的,但必须是 2MB 的整数倍,最小支持 4 MB。
垃圾回收特点
-
低停顿时间:ZGC最大的特点是在不增加延迟的情况下,能够处理非常大的内存数据,可以将停顿时间限制在10ms以内,对于需要快速响应的应用程序来说是非常重要;
-
可伸缩性:可以处理非常大的内存数据,适应不同规模的应用程序,从小型应用程序到大型企业级应用程序;
-
不需要分代:不需要将内存分为新生代和老年代,不需要复杂的内存回收算法;
-
并发处理:采用了并发处理的方式来进行垃圾回收可以在应用程序运行的同时进行垃圾回收;
工作流程
-
初始标记(STW):找 GC Roots 直接引用的对象,处理时间和GC Roots的数量成正比,停顿时间不随着堆的大小而增加;
-
并发标记(没有STW):扫描剩余的所有对象,处理时间比较长,业务线程与GC线程同时运行,但这个阶段会有漏标问题;
-
再标记(STW):通过算法解决漏标对象,和G1中的解决漏标的算法类似;
-
并发转移准备(没有STW) :分析最有回收价值GC分页,即ROI计算;
-
初始转移(STW):转移初始标记的存活对象和做对象重定位,时间和GC Roots的数量成正比,时间不随堆的大小而增加;
-
并发转移(没有STW):对转移并发标记的存活对象做转移;
平台支持说明
部分版本里面是实验性参数,需要加 -XX:+UnlockExperimentalVMOptions
才可以使用
平台 | 是否支持 | 支持版本 |
---|---|---|
Linux/x64 | 是 | JDK 15 (Experimental since JDK 11) |
Linux/AArch64 | 是 | JDK 15 (Experimental since JDK 13) |
macOS/x64 | 是 | JDK 15 (Experimental since JDK 14) |
Windows/x64 | 是 | JDK 15 (Experimental since JDK 14) |
Windows/AArch64 | 是 | JDK 16 |
macOS/AArch64 | 是 | JDK 17 |
Linux/PowerPC | 是 | JDK 18 |
验证参数(JDK17环境)
参数: -XX:+UseZGC -XX:+PrintCommandLineFlags -Xms32m -Xmx32m
输出结果
-XX:InitialHeapSize=33554432 -XX:MaxHeapSize=33554432 -XX:MinHeapSize=33554432 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:-UseCompressedOops -XX:+UseZGC
关于升级建议
ZGC业界还没大规模使用,更多在实验性观望阶段,还存在变动和争议阶段,如果可能则预计26年~28年成为主流;当下我们开发的采用的垃圾收集器是G1收集器,23~25年会是主流。
大家使用的话可以升级JDK,在JDK 11中首次支持ZGC,且JDK 11属于长期支持(Long Term Support,LTS)版本,Linux平台下JDK11开始支持ZGC,JDK14开始支持Mac和Windows。
JVM调优中,升级JDK版本有较大收益,但是风险需要评估好。通常从兼容性和功能模块和性能风险进行评估。
- 兼容性和功能模块:程序依赖很多三方jar包,有些jdk升级会把旧的API直接移除,导致项目直接编译不通过,版本升级,有些潜在的功能逻辑可能会被调整,导致些不可预测的故障产生,尤其是接锅侠身上;
- 性能风险:短期测试相关数据是比较优,但是也需要能保持性能稳定。常规建议技术团队可以预发布环境进行稳定性测试,基于生产环境流量拷贝;
学习资料拓展:https://wiki.openjdk.org/display/zgc