基于jdk17的JVM GC配置详解
本文章是基于JDK17的JVM虚拟机中GC参数的详细解读,内容有误的欢迎指正。
一、垃圾回收器(GC)
是自动管理应用程序的动态内存分配请求。垃圾回收器通过以下操作执行自动动态内存管理:
- 从操作系统分配内存并将内存返回给操作系统。
- 当应用程序请求时,将内存分配给它。
- 确定应用程序仍在使用该内存的哪些部分。
- 回收未使用的内存以供应用程序重用。
Java HotSpot垃圾回收器采用一下技术来提高这些操作的效率:
- 将分代清理与老年化结合使用,将他们的工作集中在堆中最可能包含大量可回收内存区域的区域上。
- 使用多线程尽可能使操作并行,或者在后台与应用程序并行执行一些长时间运行的操作。
- 尝试通过压缩活动对象来恢复较大的连续可用内存。
二、JVM默认选项
JDK17的JVM为垃圾回收器、堆大小和运行时编译器提供了依赖于平台的默认选项。这些选项符合不同类型应用程序的需要以减少命令行调优。此外,基于行为的动态调整堆的大小使其最优化,以满足应用程序的指定行为。
1、垃圾回收器、堆和运行时编译器默认选项
选项 | 默认值(基于64位操作系统) |
---|---|
垃圾回收器 | Garbage-First (G1)收集器 |
最大线程 | GC线程的最大数量受堆大小和可用CPU资源的限制 |
初始堆内存 | 物理内存的1/64的初始堆大小 |
最大堆内存 | 最大堆大小为物理内存的1/4 |
编译器 | 分层编译器,同时使用C1和C2 |
可以使用一下选项查看jvm所有的默认值
//打印最终值,如果某个默认值被新值覆盖,显示新值
-XX:+PrintFlagsFinal
//打印那些被新值覆盖的项
-XX:+PrintCommandLineFlags
//例
java -XX:+PrintFlagsFinal -version
//linux上可以查看指定参数默认值
java -XX:+PrintFlagsFinal <GC options> -version | grep MaxHeapSize
2、基于行为的调优
Java HotSpot VM垃圾回收器可以配置为优先满足以下两个需求之一:最大暂停时间和应用程序吞吐量。如果达到了首选目标,回收器将尝试资源利用率最大化。应用程序需要最小的堆来容纳至少所有的实时数据,而其他配置可能会阻碍实现部分或全部所需目标。
- 最大暂停时间目标
暂停时间是指垃圾回收器停止应用程序并恢复不再使用的空间的持续时间。最大暂停时间目标的目的是限制这些暂停中最长的一个暂停时间。
//最长暂停时间目标(<nnn>毫秒或更短的暂停时间;默认值因垃圾回收器而异)
-XX:MaxGCPauseMillis=<nnn>
- 吞吐量目标
吞吐量目标是根据回收垃圾所花费的时间来衡量的,而在垃圾回收之外所花费的时间就是应用程序时间
//吞吐量目标指定。垃圾回收时间与应用程序时间的比率为 1/(1+nnn),当然,比率不是越小也好,受堆内存大小、分配、程序等因素影响
-XX:GCTimeRatio=nnn
- 追踪
如果已经达到吞吐量和最大暂停时间目标,那么垃圾回收器会减小堆的大小,直到其中一个目标(总是吞吐量目标)无法达到为止,使内存利用率最大化。需设置垃圾回收器可以使用的最小和最大堆大小。
//垃圾回收器可以使用的最小堆大小
-Xms=<nnn>
//垃圾回收器可以使用的最大堆大小
-Xmx=<mmm>
注!:可以将-Xms和-Xmx设置为相同的值来推断应用程序需要多少堆才能正常工作
3、调优策略
- 堆增长或缩小到支持所选吞吐量目标的大小
- 不要随意为堆设置最大值,除非您知道需要一个大于默认最大堆大小的堆
- 为应用程序选择一个足够的吞吐量目标
- 应用程序行为的更改可能会导致堆增长或收缩
- 如果堆增长到其最大大小,并且没有达到吞吐量目标,那么最大堆大小对于吞吐量目标来说太小了,此时堆内存如果接近平台上总物理内存的值了,那吞吐量目标可能过高了
- 如果可以达到吞吐量目标,但暂停时间过长,则选择最大暂停时间目标。对应用程序来说可以接受的折衷值
三、垃圾回收器实现
Java HotSpot虚拟机包含了许多不同的垃圾收集算法,除了ZGC之外,所有这些算法都使用了分代收集的技术;内存是按代管理的(内存池包含不同年龄的对象)。当其中一个代填满时就对该代进行垃圾回收。
图3-1
堆的整个地址空间在逻辑上分为新生代和老年代。新生代由eden区和两个幸存者空间(survivor)组成。
在任何时候其中一个幸存者空间是空的,并在垃圾回收期间作为eden中和另一个幸存者空间活动对象的存储空间;垃圾回收后,eden和源幸存者空间为空。在下一个垃圾收集中,交换两个幸存空间的用途。对象在幸存者空间之间进行复制,直到被复制了一定次数或没有足够的空间。这些对象将复制到老年代中。这个过程也被称为老化。
四、影响垃圾回回收性能的因素
影响垃圾收集性能的两个最重要的因素是总可用内存和专用于新生代的堆的比例
1、总可用堆内存
如果-Xms参数的值小于-Xmx参数的值,则并非所有保留的空间都会立即提交给虚拟机。未提交的空间在图3-1被标记为虚拟区“virtual”;老年代和新生代可以根据需要增长到虚拟空间极限。
//堆空闲率大于最小空闲率就会尝试缩小堆内存,总大小的下限为–Xms<min>,上限为–Xmx<max>。
//最小堆空闲率 默认值40%
-XX:MinHeapFreeRatio=<minimum>
//最大堆空闲率 默认值为70%( 总大小的下限为–Xms<min>,上限为–Xmx<max>)
-XX:MaxHeapFreeRatio=<maximum>
//立即将Java堆减小到目标大小(由参数-XX:MaxHeapFreeRatio指定)。使用此设置可能会导致性能下降
-XX:-SrinkHeapInSteps
2、新生代堆内存
新生代的堆空间越大, minor GC的次数就会越少。在保证老年代空间足够的情况下或合适的major GC频率时适当扩大新生代堆内存,这将增加主要集合的频率。最佳选择取决于应用程序分配的对象的生存期分布。
//老一代与新生代的相对大小(-XX:NewRatio=3表示新生代和老年代之间的比例为1:3)
–XX:NewRatio
//绑定新生代的大小范围。将它们设置为相同的值可以决定新生代大小,就像将-Xms和-Xmx设置为相同值可以决定总堆大小
-XX:NewSize
-XX:MaxNewSize
//Survivor 幸存者空间大小
//调整幸存者空间的大小,但通常这对性能并不重要(-XX:SurvivorRatio=n 即新生代分为(n+2)等份 每个幸存者空间的大小将是eden的n分之一,是新生代的n+2分之一 )
-XX:SurvivorRatio
新生代堆大小的默认值
选项 | 默认值 |
---|---|
-XX:NewRatio | 2 |
-XX:NewSize | 1310MB |
-XX:MaxNewSize | 无限制 |
-XX:SurvivorRatio | 8 |
以下是服务器应用程序的通用指南:
-
首先决定您可以为虚拟机提供的最大堆大小。然后,根据新生代的规模绘制您的性能指标,以找到最佳设置。
请注意,最大堆大小应始终小于机器上安装的内存量,以避免出现过多的页面错误和抖动。
-
如果总堆大小是固定的,那么增加新生代的大小就需要减少旧一代的尺寸。保持旧一代足够大,以容纳应用程序在任何给定时间使用的所有实时数据,再加上一定的空闲空间(10%到20%或更多)。
-
根据前面所述的对老年代的限制::
- 给新生代足够的内存。
- 随着处理器数量的增加,增加新生代的规模,因为分配可以并行化。
五、可选垃圾回收器
1、Serial 垃圾收集器
单线程处理所有垃圾回收工作,因为线程之间没有通信开销。
它最适合单处理器机器,因为它不能利用多处理器硬件,它在多处理器上对具有一个小数据集(最大约100 MB)的应用程序很有用。在某些硬件和操作系统配置中,默认情况下会选择串行收集器
//指定
-XX:+UseSerialGC.
2. Parallel 垃圾回收器
Parallel GC并行垃圾回收器。Java堆的垃圾收集采用并行方式执行,以提高吞吐量和响应性。并行收集器适用于具有在多处理器或多线程硬件上运行的中型到大型数据集的应用程序。
//启用并行垃圾收集器(Parallel GC) 默认:新生代Parallel GC + 老年代ParallelOld GC
-XX:+UseParallelGC
//禁用在老年代使用并行垃圾回收器(Parallel GC) 老年代使用SerialOld GC
-XX:-UseParallelOldGC
//线程数
//默认:在具有N核处理器(其中N大于8时)的计算机上, Parallel 垃圾回收器使用N的固定部分(大约 5/8) 作为垃圾收集器线程数。当<N>的值低于8时,使用的数字为<N>。在选定的平台上,这一比例降至5/16。
-XX:ParallelGCThreads=<N>
//代的规模的增长和缩小是通过增量来完成的,增量是代规模的固定百分比,这样代就可以朝着自己想要的规模递增或递减
//默认:代内存以20%的增量增长,以5%的增量收缩
//新生代扩展率
-XX:YoungGenerationSizeIncrement=<Y>
//老年代扩展率
-XX:TenuredGenerationSizeIncrement=<T>
//收缩比例 如果增长增量为X%,则收缩的减量为X/D%
-XX:AdaptiveSizeDecrementScaleFactor=<D>
//ParallelGC 时间过长和内存不足错误
//默认:果超过98%的总时间用于垃圾收集,而回收的堆不到2%,则会引发OutOfMemoryError,为了防止应用程序由于堆太小回收率很低导致长时间运行
//如果有必要,可以通过在命令行中添加选项来禁用此功能
-XX:-UseGCOverheadLimit
3. Garbage-First (G1) 垃圾回收器
G1)垃圾回收器适用于小型到大型具有大内存和多处理器机器,响应时间比总吞吐量更重要,并且垃圾收集暂停时间必须更短的应用程序。提供了高概率达到暂停时间目标的能力,同时实现高吞吐量。
G1堆布局:划分为一组大小相等的堆区域(regions),每个区域都是一个连续的虚拟内存范围,区域是内存分配和回收为单位。在任何时候,这些区域中的每一个都可以是空的(浅灰色)、或者分配给某一代;
浅灰色:空闲
红色:eden
红色(S):幸存空间
浅蓝色:老年代
浅蓝色(H):老年代(大对象)连续内存
//设置项显式启用G1
-XX:+UseG1GC
//G1收集器只考虑在Remark和FullGC暂停期间调整Java堆的大小。此过程可以向操作系统释放内存或从操作系统分配内存
//最小Java堆大小
-XX:InitialHeapSize
//最大Java堆大小
-XX:MaxHeapSize
//最小可用内存百分比
-XX:MinHeapFreeRatio
//确定调整大小后可用内存的最大百分比
-XX:MaxHeapFreeRatio
//最长暂停时间 默认200
-XX:MaxGCPauseMillis=200
//设置的暂停时间目标
-XX:PauseTimeIntervalMillis
-XX:MaxGCPauseTimeMillis
//自适应地调整新生代代的大小,以满足暂停时间 默认5
-XX:G1NewSizePercent=5
-XX:G1MaxNewSizePercent=60
//谨慎使用以下控制,这将会使暂停时间控制配置失效
-XX:NewSize
-XX:MaxNewSize
//在一次垃圾回收过程中,允许执行多少次混合回收;默认:8
-XX:G1MixedGCCountTarget=8
//用于并发工作的最大线程数。默认情况下,此值为-XX:PalallelGCThreads除以4
-XX:ConcGCThreads=<ergo>
//G1堆中允许的最大浪费百分比 默认值为:5%
//当Eden区中存活的对象数量超过这个比例时,G1垃圾回收器会触发Minor GC,将Eden区中存活的对象移动到Survivor区或老年代
-XX:G1HeapWastePercent=5
//定期垃圾回收
//G1考虑执行垃圾收集的最小间隔(以毫秒为单位) 如果自上一次垃圾收集暂停以来经过了这段时间,并且没有正在进行的并发循环,G1将触发额外的垃圾收集
-XX:G1PeriodicGCInterval =0
//在执行周期性GC时是否采用并发模式 默认+
-XX:+G1PeriodicGCInvokesConcurrent
//G1垃圾回收器在执行周期性GC时,系统负载超过该阈值时触发GC;默认值为95
-XX:G1PeriodicGCSystemLoadThreshold=95
//确定初始堆占用
//在初始化G1垃圾回收器时,堆内存的占用比例; 默认值为45
-XX:InitiatingHeapOccupancyPercent=45
//禁用G1垃圾回收器的自适应IHOP(Inter-Region Allocation Portion)功能
-XX:-G1UseAdaptiveIHOP
//G1堆内存中保留的百分比,默认值为10; 保留部分会根据应用程序的需求和堆内存的使用情况,用于动态地调整Region的大小
-XX:G1HeapReservePercent
//大对象控制
//控制G1垃圾回收器中的堆内存区域(Region)大小
//默认值基于最大堆大小,并计算为渲染大约2048个区域。大小必须是2的幂,有效值为1到32 MB
-XX:G1HeapRegionSize
//控制G1垃圾回收器对巨大对象的回收策略。 默认是true
//设置为true时,会对巨大对象进行独立的回收。这样可以减少垃圾回收的停顿时间,但可能会导致内存占用较高。
//设置为false时,不会对巨大对象进行独立的回收。这可能会导致停顿时间较长,但可以减少内存占用
-XX:G1EagerReclaimHumongousObjects=true (-XX:+G1EagerReclaimHumongousObjects)
//默认情况下禁用字符串重复数据消除
-XX:+G1EnableStringDeduplication
//控制要回收的Region,必须是存活对象低于85%的Region才可以进行垃圾回收
-XX:G1MixedGCLiveThresholdPercent=85
//用来减轻因System.gc(),并且无法修改应用程序源导致Full gc的影响
-XX:+ExplicitGCInvokesConcurrent
//或者让VM完全忽略它们
-XX:+DisableExplicitGC
//用于启用总是进行预触摸的行为 (可能会导致额外的内存访问,但可以减少在并发标记阶段的引入延迟)
-XX:+AlwaysPreTouch
//每个线程都会维护一个引用对象列表,用于记录当前线程引用的对象。在垃圾回收过程中,这些引用对象列表可以帮助虚拟机更快地找到所有引用的对象,以便进行正确的回收
//控制每个线程在垃圾回收时维护的引用对象数量上限;默认1000个引用对象
-XX:ReferencesPerThread=1000
//完全禁用并行化
-XX:+ParallelRefProcEnabled
//自行补脑 High Update RS and Scan RS Times
-XX:G1RSetUpdatingPauseTimePercent=10
-XX:-ReduceInitialCardMarks
-XX:G1SummarizeRSetStatsPeriod=0
-XX:G1RSetRegionEntries
-XX:-G1UseAdaptiveConcRefinement
-XX:G1ConcRefinementGreenZone=2G
-XX:G1ConcRefinementYellowZone=<ergo>
-XX:G1ConcRefinementRedZone=<ergo>
-XX:G1ConcRefinementThreads=<ergo>
-XX:+UseLargePages
混合回收:是G1垃圾回收器的一种特殊模式,它会先停止系统运行,混合回收一些Region,再恢复系统运行,再停止系统运行,混合回收一些Region,往返重复执行。
自适应IHOP:是G1垃圾回收器的一个功能,它可以根据堆内存的使用情况动态调整Region的大小,以更好地平衡垃圾回收的效率和系统的停顿时间
预触摸:是Java虚拟机的一种优化技术,用于在并发标记阶段,通过预先访问堆内存中的对象,以减少在并发标记阶段由于对象访问而引入的延迟。这种优化技术有助于提高垃圾回收的性能
4. ZGC
ZGC(Z Garbage Collector)是Java HotSpot虚拟机中的一种新型垃圾收集器,它旨在提供更高效的内存回收性能,同时满足低延迟和一致性要求。ZGC采用了并发的、可扩展的、多线程的收集策略,可以在不进行全局停顿的情况下完成内存回收。
- 并发的垃圾回收:ZGC将垃圾收集过程划分为多个阶段,并在各个阶段之间进行并发执行。这使得在进行垃圾回收时,应用程序可以继续运行,从而降低了停顿时间。
- 内存一致性:ZGC通过使用读写屏障(Read-Write Barrier)和染色指针(Colored Pointer)等技术,确保了在并发模式下,内存一致性得到维护。这有助于开发人员避免一些并发编程中的问题。
- 扩展性:ZGC设计为可扩展的,可以适应不同大小的内存和不同的硬件环境。它使用了一种可扩展的、并行的标记算法,可以在大规模内存环境中高效运行。
- 多线程:ZGC充分利用了多线程的优势,在各个阶段都使用了并行处理和多线程技术,以提高垃圾回收的效率。
//设置项显式启用ZGC
-XX:+UseZGC
//默认情况下,ZGC取消对未使用内存的限制,将其返回到操作系统
//禁用此功能
-XX:-ZUncommit
//取消限制延迟。此延迟指定内存在符合取消限制条件之前应该使用多长时间
-XX:ZUncommitDelay=<seconds>(默认值为300秒)
六、日志输出配置
可用的日志级别:
- off
- trace
- debug
- info
- warning
- error
-XX:+PrintGC //输出GC日志。
-XX:+PrintGCDetails //输出GC的详细日志。
-XX:+PrintGCTimeStamps //输出GC的时间戳(以基准时间的形式)。
-XX:+PrintGCDateStamps //输出GC的时间戳(以日期的形式,如2018-06-19T21:53:59.230+0800)。
-XX:+PrintHeapAtGC //在进行GC的前后打印出堆的信息
//日志输出
-Xlog:gc*=debug
//记录Java堆上对象所占据的区域数
gc+heap=info
//中包括来自操作系统的信息,并详细说明暂停期间的时间
gc+cpu=info
//获得有关新生代或老年代区域的疏散对暂停时间的贡献的信息
gc+ergo+cset=trace
gc+region=trace
gc+task*=debug
gc+ref*=debug
gc+stringdedup*=debug
jit+compilation=info
//JVM 会在每个安全点处打印一些调试信息,帮助开发人员了解程序的执行情况
safepoint=debug
//打印不同的phase级别信息。这提供了在debug级别记录的详细信息级别。
gc+phases=debug
//以trace级别同时打印gc和ergo消息的组合。该信息包括有关堆大小和收集集构造的所有详细信息。
gc+ergo*=trace
//以trace级别 打印存活区的大小、以及存活对象在存活区的年龄分布
gc+age=trace
exceptions=info
class+load=level
class+preorder=debug
...
//完整示例
-Xlog:gc*=debug:file={LOG_PATH}/gc%t.log:utctime,level,tags:filecount=50,filesize=100M
-Xlog 支持以下类型的输出:
- stdout :将输出发送到标准输出
- stderr :将输出发送到stderr
- file=filename :将输出发送到文本文件
如文章对您有帮助,欢迎点赞+收藏+关注三连,您的鼓励是我创作的最大动力!
注!转发请标注出处!