分享我的 JVM 调优思路
约 7429 字大约 25 分钟
2026-05-25
看完《构建JVM知识体系》(在新窗口打开)这篇文章以后,就来到了调优部分,我们需要对JVM原理知识学以致用,转换到实战中,这样才能更好的理解JVM的运行机制,更好的调优。 这一篇文章汇总了我的调优思路,调优的方向,以及调优的实操。
JVM调优
什么情况下调优
程序在上线前的测试或运行中有时会出现一些大大小小的JVM问题,比如cpu load过高、请求延迟、tps降低等,甚至出现内存泄漏(每次垃圾收集使用的时间越来越长,垃圾收集频率越来越高,每次垃圾收集清理掉的垃圾数据越来越少)、内存溢出导致系统崩溃,因此需要对JVM进行调优,使得程序在正常运行的前提下,获得更高的用户体验和运行效率。
- Heap内存(老年代)持续上涨达到设置的最大内存值
- Full GC 次数频繁
- GC 停顿(Stop World)时间过长(超过1秒,具体值按应用场景而定)
- 应用出现OutOfMemory 等内存异常
- 应用出现OutOfDirectMemoryError等内存异常( failed to allocate 16777216 byte(s) of direct memory (used: 1056964615, max: 1073741824))
- 应用中有使用本地缓存且占用大量内存空间
- 系统吞吐量与响应性能不高或下降
- 应用的CPU占用过高不下或内存占用过高不下
在JVM调优时,需要关注哪些指标?
- 吞吐量: 用户代码时间 / (用户代码执行时间 + 垃圾回收时间)。是评价垃圾收集器能力的重要指标之一,是不考虑垃圾收集引起的停顿时间或内存消耗,垃圾收集器能支撑应用程序达到的最高性能指标。吞吐量越高算法越好。
- 低延迟: STW越短,响应时间越好。评价垃圾收集器能力的重要指标,度量标准是缩短由于垃圾收集引起的停顿时间或完全消除因垃圾收集所引起的停顿,避免应用程序运行时发生抖动。暂停时间越短算法越好
- 在设计(或使用)GC 算法时,我们必须确定我们的目标:一个 GC 算法只可能针对两个目标之一(即只专注于最大吞吐量或最小暂停时间),或尝试找到一个二者的折衷
- MinorGC尽可能多的收集垃圾对象。我们把这个称作MinorGC原则,遵守这一原则可以降低应用程序FullGC 的发生频率。FullGC 较耗时,是应用程序无法达到延迟要求或吞吐量的罪魁祸首
- 堆大小调整的着手点、分析点:
- 统计Minor GC 持续时间
- 统计Minor GC 的次数
- 统计Full GC的最长持续时间
- 统计最差情况下Full GC频率
- 统计GC持续时间和频率对优化堆的大小是主要着手点
- 我们按照业务系统对延迟和吞吐量的需求,在按照这些分析我们可以进行各个区大小的调整
- 一般来说吞吐量优先的垃圾回收器:-XX:+UseParallelGC -XX:+UseParallelOldGC,即常规的(PS/PO)
- 响应时间优先的垃圾回收器:CMS、G1
调优目的
对 JVM 的调优主要是为了减少GC时间、GC次数,另一个种说法也就是 提高吞吐量 和 降低停顿延迟。
调优思路
年轻代:
GC时间、GC次数是相悖的,GC时间小必须要有一个更小的年轻代,要GC次数少,必须要有一个更大的年轻代,我们需要结合服务去取其平衡点。
老年代:
在更小的年轻代时必然导致更大的老年代,增加 Full GC 时间,更大的年轻代也会导致更小的老年代,会增加 Full GC 的频率,同样会有以上悖论。
抉择:
本着 Full GC 尽量少的原则,观察应用,看应用峰值时年轻代、老年代内存使用率,根据实际情况,加大年轻代,但是要保证预留出老年代峰值时的1/3的内存增长空间
调优指标
- 内存占用:程序正常运行需要的内存大小
- 延迟:由于垃圾收集而引起的程序停顿时间
- 吞吐量:用户程序运行时间占用户程序和垃圾收集占用总时间的比值。
当然,和CAP原则一样,同时满足一个程序内存占用小、延迟低、高吞吐量是不可能的,程序的目标不同,调优时所考虑的方向也不同,在调优之前,必须要结合实际场景,有明确的的优化目标,找到性能瓶颈,对瓶颈有针对性的优化,最后进行测试,通过各种监控工具确认调优后的结果是否符合目标。
::: tip怎么设计一个服务的堆内存占用大小 确认业务服务的峰值,比如说:订单服务,某时刻峰值后进行垃圾回收,剩余存活对象会进入到 Survivor 区域,同时存在两种情况:
- 如果 Survivor 区域内存不够,分代年龄占用一半以上的对象会上升到老年代
- 如果 Survivor 区域内存足够,根据当前 Survivor 区域的存活对象占用内存来算
第一种情况,内存不够需要提高内存,再次查看峰值后的内存回收情况;当第一种情况调整后,进入第二种内存足够的情况,结合峰值后存活对象的内存与预留1/3内存算 Survivor 区域的整体内存,而 Survivor 区域只占用年轻代的 10% 内存,基于峰值后的内存加上预留的内存为Survivor区域10%的内存开始推敲,就能知道年轻代需要设置多少,通过默认3/8堆内存比例占用,能推敲出老年代是多少内存,最终确认一个服务堆的内存占用 :::
调优方向
将新对象预留在年轻代
众所周知,由于 Full GC 的成本远远高于 Minor GC,因此某些情况下需要 尽可能将对象分配在年轻代。在大部分情况下,JVM 会尝试在 Eden 区分配对象,但是由于空间紧张等问题,很可能不得不将部分年轻对象提前向年老代压缩。因此,在 JVM 参数调优时可以为应用程序分配一个合理的年轻代空间,以最大限度避免新对象直接进入老年代的情况发生。
对象从年轻代晋升到老年代有3种方式,一种是达到分代年龄值,一种是大对象直接进入到老年代,还有一种是动态年龄判断
动态对象年龄判断,即在survivor区中低于或等于某年龄的的所有对象大小的总和大于Survivor空间的一半时,大于或等于改年龄的对象就可直接进入老年代,无需等到
-XX:MaxTenuringThreshold中要求的年龄
例如:当在Minor GC发生时,年龄小于等于5岁的对象大小总和超过了survivor区总内存的一半,那么年龄大于等于5岁的对象即可以直接进入老年代。
调优时可根据应用对象的使用适当修改分代年龄 -XX:MaxTenuringThreshold 或者修改提高 from 区的利用率,使 from 区使用到 90%时,再将对象送入年老代 -XX:TargetSurvivorRatio=90
将大对象直接进入到老年代
基于以上原因,可以将大对象直接分配到年老代,保持年轻代对象结构的完整性,这样可以提高 GC 的效率。
如果一个大对象同时又是一个短命的对象,假设这种情况出现很频繁,那对于 GC 来说会是一场灾难。
原本应该用于存放永久对象的年老代,被短命的对象塞满,这也意味着对堆空间进行了洗牌,扰乱了分代内存回收的基本思路。因此,在软件开发过程中,应该尽可能避免使用短命的大对象。
通过 -XX:PetenureSizeThreshold 设置大对象直接进入年老代的阈值。当对象的大小超过这个值时,将直接在年老代分配。
参数 -XX:PetenureSizeThreshold 只对串行收集器和年轻代并行收集器有效,并行回收收集器不识别这个参数。
设置稳定的Java堆
稳定的堆可以减少GC次数,将 -Xms 与 -Xmx 设置大小一样,即最大堆内存与初始堆内存一样,就可以获得一个稳定的堆
稳定的堆大小虽然可以减少 GC 次数,但同时也增加了每次GC的时间。
让堆大小在一个区间中震荡,在系统不需要使用大内存时,压缩堆空间,使 GC 应对一个较小的堆,可以加快单次 GC 的速度。基于这样的考虑,JVM 还提供了两个参数用于压缩和扩展堆空间。
-XX:MinHeapFreeRatio 参数用来设置堆空间最小空闲比例,默认值是 40。当堆空间的空闲内存小于这个数值时,JVM 便会扩展堆空间。
-XX:MaxHeapFreeRatio 参数用来设置堆空间最大空闲比例,默认值是 70。当堆空间的空闲内存大于这个数值时,便会压缩堆空间,得到一个较小的堆。
当-Xmx 和-Xms 相等时,-XX:MinHeapFreeRatio 和 -XX:MaxHeapFreeRatio 两个参数无效
选择适当的垃圾回收器
吞吐量优先:
吞吐量优先的方案将会尽可能减少系统执行垃圾回收的总时间,故可以考虑关注系统吞吐量的并行回收(Parallel Scavenge)收集器。在拥有高性能的计算机上,进行吞吐量优先优化
java –Xmx3800m –Xms3800m –Xmn2G –Xss128k –XX:+UseParallelGC –XX:ParallelGC-Threads=20 –XX:+UseParallelOldGC–Xmx3800m –Xms3800m设置 Java 堆的最大值和初始值。一般情况下,为了避免堆内存的频繁震荡,导致系统性能下降,我们的做法是设置最大堆等于最小堆-Xss128k减少线程栈的大小,这样可以使剩余的系统内存支持更多的线程;-Xmn2g设置年轻代区域大小为 2GB;–XX:+UseParallelGC年轻代使用并行垃圾回收收集器。这是一个关注吞吐量的收集器,可以尽可能地减少 GC 时间。 (Parallel Scavenge + Serial Old (PS MarkSweep))-XX:+UseParallelOldGC设置年老代使用并行回收收集器(Parallel Scavenge + Parallel Old)。–XX:ParallelGC-Threads设置用于垃圾回收的线程数,通常情况下,可以设置和 CPU 数量相等。但在 CPU 数量比较多的情况下,设置相对较小的数值也是合理的;
停顿时间优先:
为降低应用软件的垃圾回收时的停顿,首先考虑的是使用关注系统停顿的 CMS 回收器,其次,为了减少 Full GC 次数,应尽可能将对象预留在年轻代,因为年轻代 Minor GC 的成本远远小于年老代的 Full GC。
java –Xmx3550m –Xms3550m –Xmn2g –Xss128k –XX:ParallelGCThreads=20 –XX:+UseConcMarkSweepGC –XX:+UseParNewGC –XX:+SurvivorRatio=8 –XX:TargetSurvivorRatio=90 –XX:MaxTenuringThreshold=31–XX:ParallelGCThreads=20设置 20 个线程进行垃圾回收;–XX:+UseParNewGC年轻代使用并行回收器;–XX:+UseConcMarkSweepGC年老代使用 CMS 收集器降低停顿 (ParNew + CMS + Serial Old);–XX:+SurvivorRatio设置 Eden 区和 Survivor 区的比例为 8:1。稍大的 Survivor 空间可以提高在年轻代回收生命周期较短的对象的可能性,如果 Survivor 不够大,一些短命的对象可能直接进入年老代,这对系统来说是不利的。–XX:TargetSurvivorRatio=90设置 Survivor 区的可使用率。这里设置为 90%,则允许 90%的 Survivor 空间被使用。默认值是 50%。故该设置提高了 Survivor 区的使用率。当存放的对象超过这个百分比,则对象会向年老代压缩。因此,这个选项更有助于将对象留在年轻代。–XX:MaxTenuringThreshold设置年轻对象晋升到年老代的年龄。默认值是 15 次,即对象经过 15 次 Minor GC 依然存活,则进入年老代。这里设置为 31,目的是让对象尽可能地保存在年轻代区域。
老年代如果使用CMS收集器,新生代可以不用太大,因为CMS的并行收集速度也很快,收集过程比较耗时的并发标记和并发清除阶段都可以与用户线程并发执行
代码调优
避免创建过大的对象及数组
过大的对象或数组在新生代没有足够空间容纳时会直接进入老年代,如果是短命的大对象,会提前出发Full GC。
避免同时加载大量数据
如一次从数据库中取出大量数据,或者一次从Excel中读取大量记录,可以分批读取,用完尽快清空引用。
清除引用
当集合中有对象的引用,这些对象使用完之后要尽快把集合中的引用清空,这些无用对象尽快回收避免进入老年代。
选择合适的引用创建对象
可以在合适的场景(如实现缓存)采用软引用、弱引用,比如用软引用来为ObjectA分配实例:SoftReference objectA=new SoftReference(); 在发生内存溢出前,会将objectA列入回收范围进行二次回收,如果这次回收还没有足够内存,才会抛出内存溢出的异常。
避免产生死循环
产生死循环后,循环体内可能重复产生大量实例,导致内存空间被迅速占满。
尽量避免长时间等待外部资源(数据库、网络、设备资源等)
缩小对象的生命周期,避免进入老年代,如果不能及时返回结果可以适当采用异步处理的方式等。
JDK命令及使用
jps : 虚拟机进程状况工具
输出 JVM 进程信息,hostid 不指定默认为当前主机
jps [option] [hostid]
option 参数:
- -q 不输出类名、Jar名和传入main方法的参数
- -m 输出传入main方法的参数
- -l 输出main类或Jar的全限名
- -v 输出传入JVM的参数
jstack : Java堆栈跟踪工具
查看某个Java进程内的线程堆栈信息
jstack 可以定位到线程堆栈,根据堆栈信息我们可以定位到具体代码,所以它在JVM性能调优中使用得非常多。
从JDK5起,java.lang.Thread 类新增了一个getAllStackTraces()方法用于获取虚拟机中所有线程的StackTraceElement对象。使用这个方法可以通过简单的几行代码完成jstack的大部分功能,在实际项目中不妨调用这个方法做个管理员页面,可以随时使用浏览器来查看线程堆栈。
jstack [option] pid
option 参数:
- -l 会打印出额外的锁信息,在发生死锁时可以用
jstack -l pid来观察锁持有情况 - -m 不仅会输出Java堆栈信息,还会输出C/C++堆栈信息(比如Native方法)
实战:
下面我们来一个实例找出某个Java进程中最耗费CPU的Java线程并定位堆栈信息,用到的命令有ps、top、printf、jstack、grep。
1、查找Java进程
jps -lv
得到进程号ID,也就是pid
2、查找该进程内最耗费 CPU 的线程
top -Hp pid
执行命令查看 CPU 使用率,根据最高的 CPU 使用率查看线程PID
top - 10:56:48 up 509 days, 17:06, 4 users, load average: 93.44, 83.74, 79.83
Threads: 251 total, 0 running, 251 sleeping, 0 stopped, 0 zombie
%Cpu(s): 61.6 us, 26.0 sy, 0.0 ni, 11.7 id, 0.0 wa, 0.0 hi, 0.3 si, 0.4 st
KiB Mem : 32781688 total, 1336860 free, 17312360 used, 14132468 buff/cache
KiB Swap: 8388604 total, 1799808 free, 6588796 used. 13119988 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
22714 root 20 0 4473816 443968 5796 S 0.0 1.4 0:00.01 java
22833 root 20 0 4473816 443968 5796 S 0.0 1.4 0:22.82 java
...3、输出十六进制
printf "%x\n" 20741
4、查找堆栈
输出进程的堆栈信息,然后根据线程ID的十六进制值grep
jstack 20611 | grep 5105 -A 10
输出查询到的线程ID堆栈信息,可定位到接口信息cn.code72...
"http-nio-8763-exec-2" #16 daemon prio=5 os_prio=0 tid=0x00007f6104d0f800 nid=0x50fd waiting on condition [0x00007f60d3efb000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at cn.code72.interview.controller.JvmController.wait(JvmController.java:27)
- locked <0x00000000edacef58> (a cn.code72.interview.controller.JvmController)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)jmap : Java内存映像工具
查看堆内存使用状况,一般结合jhat使用
jmap [option] pid
option 参数:
- -dump 生成Java堆转储快照:dump文件,
-dump:live只保存堆中的存活对象
jmap -dump:format=b,file=dumpFileName pid
jmap -dump:format=b,file=/tmp/dump.dat 20602::: danger警告 heap 如果比较大的话,就会导致这个过程比较耗时,并且执行的过程中为了保证dump的信息是可靠的,所以会暂停应用, 生产上慎用 :::
- -finalizerinfo 以ClassLoader为统计口径输出永久代的内存状态信息
- -heap 输出整个堆空间的详细信息,包括GC的使用、堆配置信息,以及内存的使用信息等
jamap -heap 20602
-
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 482344960 (460.0MB)
NewSize = 10485760 (10.0MB)
MaxNewSize = 160759808 (153.3125MB)
OldSize = 20971520 (20.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 10289152 (9.8125MB)
used = 6057184 (5.776580810546875MB)
free = 4231968 (4.035919189453125MB)
58.86961335589172% used
Eden Space:
capacity = 9175040 (8.75MB)
used = 5391464 (5.141700744628906MB)
free = 3783576 (3.6082992553710938MB)
58.762294224330354% used
From Space:
capacity = 1114112 (1.0625MB)
used = 665720 (0.6348800659179688MB)
free = 448392 (0.42761993408203125MB)
59.75341796875% used
To Space:
capacity = 1114112 (1.0625MB)
used = 0 (0.0MB)
free = 1114112 (1.0625MB)
0.0% used
tenured generation:
capacity = 22761472 (21.70703125MB)
used = 18285072 (17.438003540039062MB)
free = 4476400 (4.2690277099609375MB)
80.33343361975886% used- -histo 输出堆空间中对象的统计信息,包括类、实例数量和合计容量,
-histo:live只统计堆中的存活对象
jmap -histo:live 20602 | head -20::: danger警告 histo 会先触发一次GC,然后进行统计,生产慎用 :::
- -permstat 以ClassLoader为统计口径输出永久代的内存状态信息
::: danger警告 JVM会去统计perm区的状况,这整个过程也会比较的耗时,并且同样也会暂停应用。生产慎用 :::
- -F 当虚拟机进程对-dump选项没有任何响应时,强制执行生成dump文件
jstat : 虚拟机统计信息监测工具
查看堆内存使用监测
jstat [ generalOption | outputOptions vmid [interval[s|ms] [count]] ]
jstat -gc 20602 1000 2 #每秒输出一次gc,输出2次- class (类加载器)
- compiler (JIT)
- gc (GC堆状态)
| 显示列名 | 具体描述 |
|---|---|
| S0C | 年轻代中第一个survivor(幸存区)的容量 (字节) |
| S1C | 年轻代中第二个survivor(幸存区)的容量 (字节) |
| S0U | 年轻代中第一个survivor(幸存区)目前已使用空间 (字节) |
| S1U | 年轻代中第二个survivor(幸存区)目前已使用空间 (字节) |
| EC | 年轻代中Eden(伊甸园)的容量 (字节) |
| EU | 年轻代中Eden(伊甸园)目前已使用空间 (字节) |
| OC | Old代的容量 (字节) |
| OU | Old代目前已使用空间 (字节) |
| PC | Perm(持久代)的容量 (字节) |
| PU | Perm(持久代)目前已使用空间 (字节) |
| YGC | 从应用程序启动到采样时年轻代中gc次数 |
| YGCT | 从应用程序启动到采样时年轻代中gc所用时间(s) |
| FGC | 从应用程序启动到采样时old代(全gc)gc次数 |
| FGCT | 从应用程序启动到采样时old代(全gc)gc所用时间(s) |
| GCT | 从应用程序启动到采样时gc用的总时间(s) |
- gccapacity (各区大小)
- gccause (最近一次GC统计和原因)
- gcnew (新区统计)
- gcnewcapacity (新区大小)
- gcold (老区统计)
- gcoldcapacity (老区大小)
- gcpermcapacity (永久区大小)
- gcutil (GC统计汇总)
- printcompilation (HotSpot编译统计)
interval[s|ms] 参数:
每过n秒|毫秒获取
count 参数:
获取次数
jinfo : Java配置信息工具
实时查看和调整虚拟各项参数
jinfo [option] pid
option 参数:
- -flag 查询未被显示指定的参数的系统默认值,使用
java -XX:+PrintFlagsFinal查看默认值也是一个很好的选择。可使用-flag[+|-]name或者-flag name=value在运行期修改一部分运行期可写的虚拟机参数值。 - -sysprops 选项把虚拟机进程的 System.getProperties()的内容打印出来
JDK监测工具
jconsole
分析内存信息(各个区如Eden、Survivor、Old等内存变化情况),如果查看的是远程服务器的JVM,程序启动需要加上如下参数:
-Dcom.sun.management.jmxremote=true
-Djava.rmi.server.hostname=12.34.56.78
-Dcom.sun.management.jmxremote.port=18181
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=falsejvisualvm
jvisualvm 从JDK9开始不在打包如JDK中,仍可独立下载。
VisualVM 是功能最强大的运行监视和故障处理程序之一,它具备了通过插件扩展功能的能力,有了插件的支持,VisualVM 可以做到:
- 显示虚拟机进程以及进程的配置、环境信息(jps、jinfo)
- 监视应用程序的处理器、垃圾收集、堆、方法区以及线程的信息(jstat、jstack)
- dump以及分析堆转储快照(jmap、jhat)
- 方法级的程序运行性能分析,找出被调用最多、运行时间最长的方法
- 离线程序快照:收集程序的运行时配置、线程dump、内存dump等信息简历一个快照,可以将快照发送开发者处进行bug反馈
- 其他插件带来的无限可能性
初始状态下的 VisualVM 并没有加载任何插件,如果不在 VisualVM 上装任何扩展插件,就相当于没有安装任何应用软件的系统差不多
VisualVM 的插件可以手工进行安装,在 https://visualvm.github.io/pluginscenters.html 上下载nbm包后,点击工具->插件->已下载的菜单,在弹出对话框中指定nbm包路径便可完成安装.
JDK9 集成工具
JDK中提供了JCMD、JHSDB两个集成式的多功能工具箱,它们整合了所有基础工具所能提供的专项功能,能比老工具们更好,更强大
JVM参数
内存参数
| 参数 | 说明 | 示例 |
|---|---|---|
-Xms | 初始堆大小,默认物理内存的1/64 | -Xms512M |
-Xmx | 最大堆大小,默认物理内存的1/4 | -Xms2G |
-Xmn | 新生代内存大小,官方推荐为整个堆的3/8 | -Xmn512M |
-Xss | 线程堆栈大小,jdk1.5及之后默认1M,之前默认256k | -Xss512k |
-XX:NewRatio | 设置新生代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4 | -XX:NewRatio=3 |
-XX:SurvivorRatio | 年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:8,表示Eden:Survivor=8:1:1,一个Survivor区占整个年轻代的1/8 | -XX:SurvivorRatio=8 |
-XX:PermSize | 永久代初始值,默认为物理内存的1/64 | -XX:PermSize=128M |
-XX:MaxPermSize | 永久代最大值,默认为物理内存的1/4 | -XX:MaxPermSize=256M |
JDK9之前与之后日志参数
| JDK9之前日志参数 | JDK9后配置形式 | 说明 |
|---|---|---|
| PrintGC | -Xlog:gc | 查看GC基本信息 |
| PrintGCDetails | -Xlog:gc* | 查看GC详细信息 |
| PrintHeapAtGC | -Xlog:gc+heap=debug | 查看GC前后的堆、方法区可用容量变化 |
| G1PrintHeapRegions | Xlog:gc+region trace | |
| GlPrintRegionLivenessInfo | Xlog:gc+liveness trace | |
| GlSummarizeConcMark | Xlog:gc+marking trace | |
| G1SummarizeRSetStats | Xlog:gc+remset*=trace | |
| GCLogFileSize, NumberOfGCLogFiles, UseGCLog File Rotation | Xlog:gc*;file <file>::filecount-<count>,filesize-<filesizc in kb> | |
| PrintAdaptiveSizePolicy | Xlog;gc+ergo*-trace | 查看收集器Ergonomics机制(自动设置堆空间各分代区域大小、收集目标等内容,从Parallel收 集器开始支持)自动调节的相关信息。 |
| PrintClassHistogramAfterFullGC | Xlog:classhisto* trace | |
| PrintClassHistogramBeforeFullGC | Xlog:classhisto*-trace | |
| PrintGCApplicationConcurrentTime | Xlog:safepoint | |
| PrintGCApplicationStoppedTime | Xlog:safepoint | 查看GC过程中用户线程并发时间以及停顿的时间 |
| PrintGCDateStamps | 使用 time 修饰器 | 在GC日志中添加时间戳 |
| PrintGCTaskTimeStamps | Xlog:gc+task-trace | |
| PrintGCTimeStamps | 使用 uptime 修饰器 | JDK9以后详细信息默认打印时间 |
| PrintHeapAtGC | Xlog:ge+heap debug | 在每次GC前、后分别打印堆的信息 |
| PrintHeapAtGCExtended | Xlog:gc+heap-trace | |
| PrintINIGCStalls | Xlog:gc+jni-debug | |
| PrintOldPLAB | Xlog;ge+plab trace | |
| PrintParallelOldGCPhaseTimes | Xlog:gc+phases-trace | |
| PrintPLAB | Xlog;gc+plab trace | |
| PrintPromotionFailure | Xlog:gc+promotion debug | |
| PrintReferenceGC | Xlog;gc+ref debug | |
| PrintStringDeduplicationStatistics | Xlog:gc+stringdedup | |
| PrintTaskqueue | Xlog:gc+task+stats trace | |
| PrintTenuringDistribution | Xlog;gc+age trace | 查看熬过收集后剩余对象的年龄分布信息 |
| PrintTerinationStats | Xlog:gc+task+stats-debug | |
| PrintTLAB | Xlog;gc+tlab trace | |
| TraceAdaptiveGCBoundary | Xlog:heaptergo debug | |
| TraceDynamicGCThreads | Xlog gc+task-trace | |
| TraceMetadataHumongousAllocation | Xlog:gc+metaspacetalloc debug | |
| G1TraceConcRefinement | Xlog:gc+refine-debug | |
| GlTraceEagerReclaimHumongousObjects | Xlog:gc+humongous debug | |
| GlTraceStringSymbolTableScrubbing | Xlog:gc+stringtable=trace |
-Xlog日志格式
-Xlog:${what}:${output}:${decorators}:${output-options}如不配置, 则使用以下默认配置
-Xlog:all=warning:stdout:uptime,level,tags格式说明:
占位符之间用英文冒号分隔, 占位符之内用英文逗号分隔
| 占位符 | 解释 |
|---|---|
| $ | 包含标签还有日志级别, 英文逗号分隔. 例子1: -Xlog:gc*=info 表示包含 gc 标签的所有日志,info 级别的都会输出 例子2: -Xlog:gc+age=debug 表示同时包含且仅包含 gc 和 age 这两个标签的,debug 级别的都会输出 |
| $ | 输出类型, 英文逗号分隔 例如: -Xlog:gc*:filename 或者 -Xlog:gc*:file=filename 表示将gc标签的日志输出到指定文件 |
| $ | 日志标记, 英文逗号分隔 例如: time,level,tags |
| $ | 输出配置, 英文逗号分隔 例如:filecount=5,filesize=20M 表示保留5个文件,每个文件20M |
垃圾回收器参数
| 参数 | 说明 |
|---|---|
| UseSerialGC | 虚拟机运行在 Client横式下的默认值,打开此开关后,使用 Serial + Serialold 的收集器组合进行内存回收 |
| UseParNewGC | 打开此开关后,使用 ParNew +Serial old 的收集器组合进行内存回收,在JDK 9 后不再支持 |
| UseConcMarkSweepGC | 打开此开关后,使用 ParNew + CMS + Serial Old 的收集器组合进行内存回收。Serial Old 收集器将作为 CMS 收集器出现“Concurrent Mode Failure"失败后的后备收集器使用 |
| UseParallelGC | JDK 9之前虚拟机运行在 Server 模式下的默认值,打开此开关后,使用Parallel Scavenge +Serial Old (PS MarkSweep)的收集器组合进行内存回收 |
| UseParalleloldGc | 打开此开关后,使用 Parallel Scavenge + Parallel Old 的收集器组合进行内存回收 |
| SurvivorRatio | 新生代中 Eden 区域与 Survivor 区域的容量比值,默认为 8,代表 Eden:Survivor 8 : 1 |
| PretenureSizeThreshold | 直接晋升到老年代的对象大小,设置这个参数后,大于这个参数的对象将直接在老年代分配 |
| MaxTenuringThreshold | 晋升到老年代的对象年龄。每个对象在坚持过一次 Minor GC 之后,年龄就增加 1,当超过这个参数值时就进人老年代 |
| UseAdaptiveSizePolicy | 动态调管 Java 堆中各个区域的大小以及进人老年代的年龄 |
| HandlePromotionFailure | 是否允许分配担保失败。即老年代的剩余空间不是以应付新生代的整个Eden 和 Survivor 区的所有对象都存活的极端情况 |
| ParallelGCThreads | 设置并行 GC 时进行内存回收的线程数 |
| GCTimeRatio | GC 时间占总时间的比率,默认值为 99,即允许 1%的 GC 时间。仅在使用 Parallel Scavenge 收集器时生效 |
| MaxGCPauseMillis | 设置 GC 的最大停顿时间。仅在使用 Parallel Scavenge 收集器时生效 |
| CMSInitiatingOccupancyFraction | 设置 CMS 收集器在老年代空间被使用多少后触发圾收集。默认值为68%,仅在使用 CMS 收集器时生效 |
| UseCMSCompactAtFullCollection | 设置 CMS 收集器在完成垃圾收集后是否要进行一次内存碎片整理。仅在使用 CMS 收集器时生效,此参数从 JDK 9 开始废弃 |
| CMSFullGCsBeforeCompaction | 设置 CMS 收集器在进行若干次垃圾收集后再启动一次内存碎片整理。仅在使用 CMS 收集器时生效,此参数从 JDK 9 开始废弃 |
| CMSIncrementalMode | 设置CMS收集器为增量模式,适用于单CPU情况。 |
| UseGIGC | 使用 GI 收集器,这个是 JDK 9 后的 Server 模式默认值 |
| GlHeapRegionSizen | 设置 Region 大小,并非最终值 |
| MaxGCPauseMillis | 设置 G1 收集过程目标时间,默认值是 200ms,不是硬性条件 |
| GINewSizePercent | 新生代最小值,默认值是 5% |
| GlMaxNewSizePercent | 新生代最大值,默认值是 60% |
| ParallelGCThreads | 用户线程冻结期间并行执行的收集器线程数 |
| ConcGCThreads n | 并发标记、并发整理的执行线程数,对不同的收集器,根据其能够并发的阶段,有不同的含义 |
| lnitiatingHeapOccupancyPercent | 设置触发标记周期的 Java 堆占用率闽值。默认值是 45%。这里的 java 堆占比指的是 non young capacity bytes,包括 old+humongous |
| UscShenandoahGC | 使用 Shenandoah 收集器。这个选项在 OracleJDK 中不被支持,只能在OpenJDK 12 或者某些支持 Shenandoah 的 Backport 发行版本使用。目前仍然要配合-XX:+UnlockExperimentalVMOptions 使用 |
| ShenandoahGCHeuristics | Shenandoah 何时启动一次 GC 过程,其可选值有 adaptive、static、compact.passive、aggressive |
| UseZGC | 使用 ZGC 收集器,目前仍然要配合-XX:+UnlockExperimentalVMOptions使用 |
| UseNUMA | 启用NUMA 内存分配支持,目前只有 Parallel和 ZGC 支持,以后 G1 收集器可能也会支持该选项 |