JVM核心参数配置常用调试命令工具与调优思路

怀抱观古今,寝食展戏谑。这篇文章主要讲述JVM核心参数配置常用调试命令工具与调优思路相关的知识,希望能为你提供帮助。
JVM核心参数配置、常用调试命令工具与调优思路

  • 所有JVM相关参数,比较好看的表格分类,参考:
https://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
虽然是JDK 7的,但是也有较大的参考价值。
  • JDK官方技术手册 ,参考:
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/toc.html
这个就相当好了,第5章的java小节就有所有jvm相关参数。
1 JVM参数 1.1 日志参数
日志参数 作用
-XX:+PrintGC 查看GC基本信息
-XX:+PrintGCDetails 查看GC详细信息
-XX:+PrintGCDateStamps< br> -XX:+PrintGCTimeStamps 查看GC时间,单独使用时没有效果,可以与查看GC信息的参数结合使用
-XX:+PrintHeapAtGC 查看GC前后堆、方法区可用容量变化
-XX:+PrintGCApplicationConcurrentTime< br> -XX:+PrintGCApplicationStoppedTime 查看GC过程中用户线程并发时间以及停顿的时间
-XX:+PrintTenuringDistribution 查看熬过收集后剩余对象的年龄分布信息
-XX:+UseGCLogFileRotation 打开或关闭GC日志滚动记录功能,要求必须设置 -Xloggc参数
-XX:NumberOfGCLogFiles=5 设置滚动日志文件的个数,必须大于1,日志文件命名策略是,< filename> .0, < filename> .1, ..., < filename> .n-1,其中n是该参数的值
-XX:GCLogFileSize=20M 设置滚动日志文件的大小,必须大于8k,当前写日志文件大小超过该参数值时,日志将写入下一个文件
-Xloggc:/home/GCEASY/gc.log gc日志路径
可以用一个简单的demo验证下:
public class TmpTest public static void main(String[] args) byte[] obj = new byte[1024 * 1024]; obj = null; System.gc();

1.1.1 -XX:+PrintGC
[GC (System.gc())4352K-> 536K(125952K), 0.0007552 secs] [Full GC (System.gc())536K-> 395K(125952K), 0.0036377 secs]

1.1.2 -XX:+PrintGCDetails包含的信息有:发生了GC的区域、该区域GC前后内存变化(清除了多少空间、增加了多少空间、该区域当前内存容量)、总的堆内存变化、Metaspace内存变化以及最后面的各区域内存情况。
[GC (System.gc()) [PSYoungGen: 4352K-> 528K(38400K)] 4352K-> 536K(125952K), 0.0016053 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System.gc()) [PSYoungGen: 528K-> 0K(38400K)] [ParOldGen: 8K-> 392K(87552K)] 536K-> 392K(125952K), [Metaspace: 3237K-> 3237K(1056768K)], 0.0045798 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] Heap PSYoungGentotal 38400K, used 998K [0x0000000795580000, 0x0000000798000000, 0x00000007c0000000) eden space 33280K, 3% used [0x0000000795580000,0x0000000795679b20,0x0000000797600000) from space 5120K, 0% used [0x0000000797600000,0x0000000797600000,0x0000000797b00000) tospace 5120K, 0% used [0x0000000797b00000,0x0000000797b00000,0x0000000798000000) ParOldGentotal 87552K, used 392K [0x0000000740000000, 0x0000000745580000, 0x0000000795580000) object space 87552K, 0% used [0x0000000740000000,0x0000000740062248,0x0000000745580000) Metaspaceused 3256K, capacity 4496K, committed 4864K, reserved 1056768K class spaceused 357K, capacity 388K, committed 512K, reserved 1048576K

1.1.3 -XX:+PrintGCDateStamps与-XX:+PrintGCDetails结合使用的效果:
2020-01-13T01:10:45.422-0800: [GC (System.gc()) [PSYoungGen: 4352K-> 480K(38400K)] 4352K-> 488K(125952K), 0.0018664 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 2020-01-13T01:10:45.424-0800: [Full GC (System.gc()) [PSYoungGen: 480K-> 0K(38400K)] [ParOldGen: 8K-> 392K(87552K)] 488K-> 392K(125952K), [Metaspace: 3224K-> 3224K(1056768K)], 0.0071912 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] Heap PSYoungGentotal 38400K, used 998K [0x0000000795580000, 0x0000000798000000, 0x00000007c0000000) eden space 33280K, 3% used [0x0000000795580000,0x0000000795679b20,0x0000000797600000) from space 5120K, 0% used [0x0000000797600000,0x0000000797600000,0x0000000797b00000) tospace 5120K, 0% used [0x0000000797b00000,0x0000000797b00000,0x0000000798000000) ParOldGentotal 87552K, used 392K [0x0000000740000000, 0x0000000745580000, 0x0000000795580000) object space 87552K, 0% used [0x0000000740000000,0x0000000740062110,0x0000000745580000) Metaspaceused 3256K, capacity 4496K, committed 4864K, reserved 1056768K class spaceused 357K, capacity 388K, committed 512K, reserved 1048576K

1.1.4 -XX:+PrintHeapAtGC
Heap before GC invocations=1 (full 0): PSYoungGentotal 38400K, used 3686K [0x0000000795580000, 0x0000000798000000, 0x00000007c0000000) eden space 33280K, 11% used [0x0000000795580000,0x0000000795919a38,0x0000000797600000) from space 5120K, 0% used [0x0000000797b00000,0x0000000797b00000,0x0000000798000000) tospace 5120K, 0% used [0x0000000797600000,0x0000000797600000,0x0000000797b00000) ParOldGentotal 87552K, used 0K [0x0000000740000000, 0x0000000745580000, 0x0000000795580000) object space 87552K, 0% used [0x0000000740000000,0x0000000740000000,0x0000000745580000) Metaspaceused 3284K, capacity 4496K, committed 4864K, reserved 1056768K class spaceused 361K, capacity 388K, committed 512K, reserved 1048576K Heap after GC invocations=1 (full 0): PSYoungGentotal 38400K, used 448K [0x0000000795580000, 0x0000000798000000, 0x00000007c0000000) eden space 33280K, 0% used [0x0000000795580000,0x0000000795580000,0x0000000797600000) from space 5120K, 8% used [0x0000000797600000,0x0000000797670020,0x0000000797b00000) tospace 5120K, 0% used [0x0000000797b00000,0x0000000797b00000,0x0000000798000000) ParOldGentotal 87552K, used 8K [0x0000000740000000, 0x0000000745580000, 0x0000000795580000) object space 87552K, 0% used [0x0000000740000000,0x0000000740002000,0x0000000745580000) Metaspaceused 3284K, capacity 4496K, committed 4864K, reserved 1056768K class spaceused 361K, capacity 388K, committed 512K, reserved 1048576KHeap before GC invocations=2 (full 1): PSYoungGentotal 38400K, used 448K [0x0000000795580000, 0x0000000798000000, 0x00000007c0000000) eden space 33280K, 0% used [0x0000000795580000,0x0000000795580000,0x0000000797600000) from space 5120K, 8% used [0x0000000797600000,0x0000000797670020,0x0000000797b00000) tospace 5120K, 0% used [0x0000000797b00000,0x0000000797b00000,0x0000000798000000) ParOldGentotal 87552K, used 8K [0x0000000740000000, 0x0000000745580000, 0x0000000795580000) object space 87552K, 0% used [0x0000000740000000,0x0000000740002000,0x0000000745580000) Metaspaceused 3284K, capacity 4496K, committed 4864K, reserved 1056768K class spaceused 361K, capacity 388K, committed 512K, reserved 1048576K Heap after GC invocations=2 (full 1): PSYoungGentotal 38400K, used 0K [0x0000000795580000, 0x0000000798000000, 0x00000007c0000000) eden space 33280K, 0% used [0x0000000795580000,0x0000000795580000,0x0000000797600000) from space 5120K, 0% used [0x0000000797600000,0x0000000797600000,0x0000000797b00000) tospace 5120K, 0% used [0x0000000797b00000,0x0000000797b00000,0x0000000798000000) ParOldGentotal 87552K, used 395K [0x0000000740000000, 0x0000000745580000, 0x0000000795580000) object space 87552K, 0% used [0x0000000740000000,0x0000000740062f80,0x0000000745580000) Metaspaceused 3284K, capacity 4496K, committed 4864K, reserved 1056768K class spaceused 361K, capacity 388K, committed 512K, reserved 1048576K

1.1.5 -XX:+PrintGCApplicationConcurrentTime
Application time: 0.0031662 seconds Total time for which application threads were stopped: 0.0043566 seconds, Stopping threads took: 0.0000299 seconds Application time: 0.0016923 seconds

1.1.6 -XX:+PrintTenuringDistribution
Desired survivor size 5242880 bytes, new threshold 7 (max 15)

1.1.7 -Xloggc:filename比如使用下面的gc参数:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/Users/yeyonghao/gc.log

那么在我的系统上会有下面的日志:
$ cat gc.log Java HotSpot(TM) 64-Bit Server VM (25.181-b13) for bsd-amd64 JRE (1.8.0_181-b13), built on Jul7 2018 01:02:31 by "java_re" with gcc 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00) Memory: 4k page, physical 8388608k(141640k free)/proc/meminfo:CommandLine flags: -XX:InitialHeapSize=134217728 -XX:MaxHeapSize=2147483648 -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC 2020-01-13T01:18:33.490-0800: 0.163: [GC (System.gc()) [PSYoungGen: 4352K-> 416K(38400K)] 4352K-> 416K(125952K), 0.0028668 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2020-01-13T01:18:33.493-0800: 0.166: [Full GC (System.gc()) [PSYoungGen: 416K-> 0K(38400K)] [ParOldGen: 0K-> 388K(87552K)] 416K-> 388K(125952K), [Metaspace: 3178K-> 3178K(1056768K)], 0.0043444 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] Heap PSYoungGentotal 38400K, used 998K [0x0000000795580000, 0x0000000798000000, 0x00000007c0000000) eden space 33280K, 3% used [0x0000000795580000,0x0000000795679b28,0x0000000797600000) from space 5120K, 0% used [0x0000000797600000,0x0000000797600000,0x0000000797b00000) tospace 5120K, 0% used [0x0000000797b00000,0x0000000797b00000,0x0000000798000000) ParOldGentotal 87552K, used 388K [0x0000000740000000, 0x0000000745580000, 0x0000000795580000) object space 87552K, 0% used [0x0000000740000000,0x0000000740061000,0x0000000745580000) Metaspaceused 3192K, capacity 4496K, committed 4864K, reserved 1056768K class spaceused 354K, capacity 388K, committed 512K, reserved 1048576K

1.1.8 生产环境推荐可以给GC日志的文件后缀加上时间戳,当JVM重启以后,会生成新的日志文件,新的日志也不会覆盖老的日志,只需要在日志文件名中添加%t的后缀即可:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/GCEASY/gc-%t.log

%t会给文件名添加时间戳后缀,格式是YYYY-MM-DD_HH-MM-SS。这样就非常简单了克服了UseGCLogFileRotation存在的所有的问题!
在我的系统上尝试,会生成下面的gc文件:
gc-2020-01-13_01-23-23.log

注意它表示的是当前系统时区的本地时间,不是UTC时间。
1.2 HeapDump参数
参数 作用
-XX:+HeapDumpOnOutOfMemoryError 发生java.lang.OutOfMemoryError时,将堆转储到文件中。
-XX:HeapDumpPath=./java_pid< pid> .hprof| 保存堆转储文件的路径。默认会在当前程序运行路径下生成dump文件,如:java_pid27023.hprof`
-XX:OnOutOfMemoryError=" & lt; cmd args& gt; ; & lt; br/& gt; & lt; cmd args& gt; " 抛出OutOfMemoryError时,运行用户定义的命令(比如可以删除之前的dump文件,避免占用较多磁盘空间,或者是其它策略)
-XX:ErrorFile=./hs_err_pid& lt; pid& gt; .log 如果发生错误,将错误数据保存到文件中。
-XX:OnError=" & lt; cmd args& gt; ; & lt; cmd args& gt; " 发生错误时,运行用户定义的命令。
-XX:+CrashOnOutOfMemoryError 会在当前运行目录生成hs_err_pid& lt; pid& gt; .log日志文件
使用下面的示例程序:
public class HeapOOM static class OOMObject public static void main(String[] args) ArrayList< OOMObject> list = new ArrayList< > (); while (true) list.add(new OOMObject());

配置下面的jvm参数:
-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/log/MyApp_dump.hprof -XX:OnOutOfMemoryError="mv /tmp/log/MyApp_dump.hprof /tmp/log/MyApp_dump_%p.hprof"

执行程序后,会有下面的提示:
java.lang.OutOfMemoryError: Java heap space Dumping heap to /tmp/log/MyApp_dump.hprof ... Heap dump file created [27953782 bytes in 0.119 secs] # # java.lang.OutOfMemoryError: Java heap space # -XX:OnOutOfMemoryError="mv /tmp/log/MyApp_dump.hprof /tmp/log/MyApp_dump_%p.hprof" #Executing "mv /tmp/log/MyApp_dump.hprof /tmp/log/MyApp_dump_27587.hprof"...

这时可以看到在/tmp/log目录下有新生成的dump文件:
$ ls MyApp_dump_27587.hprof

如果再发生溢出时,还会生成新的dump文件:
$ ls MyApp_dump_27587.hprof MyApp_dump_27611.hprof

1.3 内存与收集器参数
1.3.1 收集器总览【JVM核心参数配置常用调试命令工具与调优思路】其组合如下:
JVM核心参数配置常用调试命令工具与调优思路

文章图片

1.3.2 内存参数关于Java虚拟机的内存分布情况,可以参考:
JVM核心参数配置常用调试命令工具与调优思路

文章图片

参数 作用
-Xms256m 设置初始堆内存大小
-Xmx512m 设置最大堆内存大小< br> 需要注意的是,即使当前堆内存大小还没有达到最大堆内存大小,这部分内存也会被JVM虚拟机锁定
-Xss512k 设置java虚拟机栈大小
-XX:MaxMetaspaceSize=Nm 设置元空间最大值,默认是一个很大的值,即表示不限制,或者说只受限于本地内存大小
-XX:MetaspaceSize=Nm 指定元空间的初始大小,达到该值就会触发垃圾收集进行类型卸载,同时收集器会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过-XX:MaxMetaspaceSize(如果设置了的话)的情况下,适当提高该值
-XX:MinMetaspaceFreeRatio 作用是在垃圾收集之后控制最小的元空间剩余容量的百分比,可减少因为元空间不足导致的垃圾收集的频率。当进行过Metaspace GC之后,会计算当前Metaspace的空闲空间比,如果空闲比小于这个参数,那么虚拟机将增长Metaspace的大小。在本机该参数的默认值为40,也就是40%(这个没验证过)。设置该参数可以控制Metaspace的增长的速度,太小的值会导致Metaspace增长的缓慢,Metaspace的使用逐渐趋于饱和,可能会影响之后类的加载。而太大的值会导致Metaspace增长的过快,浪费内存
-XX:MaxMetaspaceFreeRatio 用于控制最大的元空间剩余容量的百分比。当进行过Metaspace GC之后, 会计算当前Metaspace的空闲空间比,如果空闲比大于这个参数,那么虚拟机会释放Metaspace的部分空间。在本机该参数的默认值为70,也就是70%(这个没验证过)
-XX:ReservedCodeCacheSize=64m Java代码在执行时一旦被编译器编译为机器码,下一次执行的时候就会直接执行编译后的代码,也就是说,编译后的代码被缓存了起来。缓存编译后的机器码的内存区域就是codeCache。这是一块独立于Java堆之外的内存区域。除了JIT编译的代码之外,Java所使用的本地方法代码(JNI)也会存在codeCache中< br/> 可参考:https://juejin.im/post/5aebf997f265da0ba76f99db
1.3.3 收集器参数
参数 作用 特定收集器
-XX:SurvivorRatio=n 新生代中Eden区域与Survivor区域的容量比值,默认为8,代表Eden:Survivor=8:1 /
–XX:NewRatio=n 老年代与新生代的容量比值,默认为2,代表Old:Young=2:1 /
-XX:PretenureSizeThreshold=n 直接晋升到老年代的对象大小,设置这个参数后,大于这个参数的对象将直接在老年代分配。n为字节数,如3145728,即3MB 只对Serial和ParNew两款新生代收集器有效
-XX:MaxTenuringThreshold=n 晋升到老年代的对象年龄。每个对象在坚持过一次Minor GC之后,年龄就增加1,当超过这个参数值时就进入老年代 /
-XX:+HandlePromotionFailure 是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个Eden和Survivor区的所有对象都存活的极端情况。详细参考Note1的说明。 /
-XX:ParallelGCThreads=n 设置并行GC时进行内存回收的线程数,不同的JVM平台其默认值不同 /
-XX:GCTimeRatio=n GC时间占总时间的比率,默认值为99,即允许1%的GC时间。仅在使用Parallel Scavenge收集器时生效 Parallel Scavenge
-XX:+DisableExplicitGC 不允许通过调用System.gc()来触发GC /
下面参数是特定收集器
-XX:UseSerialGC 虚拟机运行在Client模式下的默认值,打开此开关后,使用Serial + Serial Old的收集器组合进行内存回收 Serial< br> Serial Old
-XX:UseParNewGC 打开此开关后,使用ParNew + Serial Old的收集器组合进行内存回收,在JDK 9后不再支持 ParNew< br> Serial Old
-XX:UseConcMarkSweepGC 打开此开关后,使用ParNew + CMS + Serial Old的收集器组合进行内存回收。Serial Old收集器将作为CMS收集器出现“Concurrent Mode Failure”失败后的后备收集器使用 ParNew< br> CMS< br> Serial Old
-XX:UseParallelGC JDK9之前虚拟机运行在Server模式下的默认值,打开此开关后,使用Parallel Scavenge + Serial Old(PS MarkSweep)的收集器组合 Parallel Scavenge< br> Serial Old
-XX:UseParallelOldGC 打开此开关后,使用Parallel Scavenge + Parallel Old的收集器 Parallel Scavenge< br> Parallel Old
-XX:MaxGCPauseMillis 设置GC的最大停顿时间。仅在使用Parallel Scavenge收集器时生效 Parallel Scavenge
-XX:CMSInitiatingOccupancyFraction=70 设置CMS收集器在老年代空间被使用多少后触发垃圾收集。默认值为68%,仅在使用CMS收集器时生效 CMS
-XX:+UseCMSInitiatingOccupancyOnly 只是用设定的回收阈值(上面指定的70%),如果不指定,JVM仅在第一次使用设定值,后续则自动调整 CMS
-XX:+UseCMSCompactAtFullCollection 设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片整理,从JDK9开始废除该参数 CMS
-XX:CMSFullGCsBeforeCompaction=n 设置CMS收集器在进行若干次垃圾收集后再启动一次内存碎片整理,从JDK9开始废除该参数 CMS
1.4 默认参数与运行参数查看
1.4.1 默认参数查看可以通过下面命令来获取当前使用的jdk的所有默认参数:
yeyonghao@yeyonghaodeMacBook-Pro:/tmp/log$ java -XX:+PrintFlagsInitial > defaultFlags.log yeyonghao@yeyonghaodeMacBook-Pro:/tmp/log$ wc -l defaultFlags.log 726 defaultFlags.log

可以看到在我使用的jdk环境有700多个参数。
比如想查看MetaspaceSize的默认大小是多少:
$ grep MetaspaceSize defaultFlags.log uintx InitialBootClassLoaderMetaspaceSize= 4194304product uintx MaxMetaspaceSize= 18446744073709551615product uintx MetaspaceSize= 21810376pd product

可以看到MetaspaceSize的默认大小为20M:
> > > 21810376 / 1024 / 1024 20

比如再看一下CompressedClassSpaceSize
$ grep CompressedClassSpaceSize defaultFlags.log uintx CompressedClassSpaceSize= 1073741824product

它的默认大小为1G:
> > > 1073741824 / 1024 / 1024 1024

1.4.2 运行参数查看使用jps -v可以查看虚拟机启动时显式指定的参数列表,但如果想知道未被显式指定参数的系统默认值,除了去找资料外,就只能使用jinfo的-flag选项进行查询了。
比如查看CompressedClassSpaceSize
$ jinfo -flag CompressedClassSpaceSize 16900 -XX:CompressedClassSpaceSize=1073741824

它的大小为1G:
> > > 1073741824 / 1024 / 1024 1024

相当于是使用了默认值。
比如查看MaxMetaspaceSize
$ jinfo -flag MaxMetaspaceSize 16900 -XX:MaxMetaspaceSize=18446744073709547520

它的默认最大空间为不限制,即使用了默认值,能分配多少空间取决于操作系统内存大小。
2 监控与调试工具 2.1 jps
jps [ options ] [ hostid ]

各参数说明如下:
选项 作用
-q 只输出LVMID,省略主类的名称
-m 输出虚拟机进程启动时传递给主类main()函数的参数
-l 输出主类的全名,如果进程执行的是JAR包,则输出JAR路径
-v 输出虚拟机进程启动时的JVM参数
一般使用得比较多的是jps -lvm
2.2 jstat
jstat [ option vmid [interval[s|ms] [count]] ]

参数interval和count代表查询间隔和次数,如果省略这2个参数,说明只查询一次。假设需要每250毫秒查询一次进程2764垃圾收集状况,一共查询20次,那命令是:
jstat -gc 2764 250 20

选项option代表用户希望查询的虚拟机信息,主要分为三类:类加载、垃圾收集、运行期编译状况。各参数说明如下:
选项 作用
-class 监视类加载、卸载数量、总空间以及类装载所耗费的时间
-gc 监视Java堆状况,包括Eden区、2个Survivor区、老年代、Metaspace等的容量,已用空间,垃圾收集时间合计等信息
-gccapacity 监视内容与-gc基本相同,但输出主要关注Java堆各个区域用到的最大、最小空间
-gcutil 监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比
-gccause 与-gcutil功能一样,但是会额外输出导致上一次垃圾收集产生的原因
-gcnew 监视新生代垃圾收集状况
-gcnewcapacity 监视内容与-gcnew基本相同,输出主要关注使用到的最大、最小空间
-gcold 监视老年代垃圾收集状况
-gcoldcapacity 监视内容与-gcold基本相同,输出主要关注使用到的最大、最小空间
-gcmetacapacity 输出方法区使用到的最大、最小空间
-compiler 输出即时编译器编译过的方法、耗时等信息
-printcompilation 输出已经被即时编译的方法
下面会详细介绍上面这些参数中部分个人目前用得比较多的,当然如果后面发现其它参数也很好用的话也会补充更新。
2.3.1 -class监视类加载、卸载数量、总空间以及类装载所耗费的时间。
$ jstat -class 550 LoadedBytesUnloadedBytesTime 47184 99829.01317.646.01

2.3.2 -gc监视Java堆状况,包括Eden区、2个Survivor区、老年代、Metaspace等的容量,已用空间,垃圾收集时间合计等信息。
$ jstat -gc 550 S0CS1CS0US1UECEUOCOUMCMUCCSCCCSUYGCYGCTFGCFGCTGCT 4352.0 4352.00.02853.3 34944.00.0235236.0205519.5289032.0 277047.8 41412.0 36925.1530.50100.0000.501

空间容量信息:
  • SOC:Survivor0区域空间容量(kB);
  • S1C:Survivor1区域空间容量(kB);
  • S0U:Survivor0区域空间使用量(kB);
  • S1U:Survivor1区域空间使用量(kB);
  • EC:Eden区域空间容量(kB);
  • EU:Eden区域空间使用量(kB);
  • OC:Old区域空间容量(kB);
  • OU:Old区域空间使用量(kB);
  • MC:Metaspace区域空间容量(kB);
  • MU:Metaspace区域空间使用量(kB);
  • CCSC:CompressedClass区域空间使容量(kB);
  • CCSU:CompressedClass区域空间使用量(kB);
垃圾收集信息:
  • YGC:Young GC事件次数;
  • YGCT:Young GC事件所用的总时间(s);
  • FGC:Full GC事件次数;
  • FGCT:Full GC事件所用的总时间(s);
  • GCT:GC事件所用的总时间(s),即YGCT + FGCT;
2.3.3 -gcutil监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比。
$ jstat -gcutil 692 S0S1EOMCCSYGCYGCTFGCFGCTGCT 0.0084.7384.6162.8495.9189.39490.40800.0000.408

使用表格来更好地展示与说明。
空间容量信息:
  • S0:Survivor0区域空间使用百分比;
  • S1:Survivor1区域空间使用百分比;
  • E:Eden区域空间使用百分比;
  • O:Old区域空间使用百分比;
  • M:Metaspace区域空间使用百分比;
  • CCS:CompressedClass区域空间使用百分比;
垃圾收集信息:
  • YGC:Young GC事件次数;
  • YGCT:Young GC事件所用的总时间(s);
  • FGC:Full GC事件次数;
  • FGCT:Full GC事件所用的总时间(s):
  • GCT:GC事件所用的总时间(s),即YGCT + FGCT;
2.3.4 -gccause与-gcutil功能一样,但是会额外输出导致上一次垃圾收集产生的原因。
$ jstat -gccause 692 S0S1EOMCCSYGCYGCTFGCFGCTGCTLGCCGCC 32.100.0088.3563.6095.9089.42520.43400.0000.434 Allocation FailureNo GC

主要对下面两个参数进行说明:
  • LGCC:触发上一次GC的原因(Cause of last garbage collection);
  • GCC:触发当前GC的原因(Cause of current garbage collection);
如果系统是通过调用System.gc()来触发GC的,那么也可以在这里看到原因。
2.3.5 -gcnew监视新生代垃圾收集状况。
$ jstat -gcnew 962 S0CS1CS0US1UTT MTTDSSECEUYGCYGCT 4352.0 4352.00.0 4352.016 2176.034944.018666.0530.502

各参数说明如下:
  • S0C:Survivor0区域空间容量(kB);
  • S1C:Survivor1区域空间容量(kB);
  • S0U:Survivor0区域空间使用量(kB);
  • S1U:Survivor1区域空间使用量(kB);
  • TT:Tenuring threshold,Survivor空间中,对象的age达到该阈值的大小就会直接晋升到老年代,其为动态计算;
  • MTT:Maximum tenuring threshold,Survivor空间中,TT的最大阈值大小;
  • DSS:Desired survivor size (kB);
    • ```c++
      // TargetSurvivorRatio默认50,意思是:在回收之后希望survivor区的占用率达到这个比例
      size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100)
      // 默认在Survivor空间中相同年龄所有对象大小总和大于Survivor空间的一半,年龄大于或等于该
      // 年龄的对象就可以直接进入老年代。这里为什么是一半?就是因为TargetSurvivorRatio设置为
      // 50,这个计算出来的空间就是Survivor的一半,可以参考后面提供的文章对JVM源码的分析。

  • EC:Eden区域空间容量 (kB);
  • EU:Eden区域空间使用量 (kB);
  • YGC:Young GC事件次数;
  • YGCT:Young GC事件所用的总时间(s);
2.3.6 -gcold监视老年代垃圾收集状况。
$ jstat -gcold 962 MCMUCCSCCCSUOCOUYGCFGCFGCTGCT 287592.0 275461.441104.036682.1261292.0186662.16000.0000.553

各参数说明如下:
  • MC: Metaspace区域空间容量(kB);
  • MU: Metaspace区域空间使用量(kB);
  • CCSC: CompressedClass区域空间使容量(kB);
  • CCSU: CompressedClass区域空间使用量(kB);
  • OC: Old区域空间容量(kB);
  • OU: Old区域空间使用量(kB);
  • YGC: Young GC事件次数;
  • FGC: Full GC事件次数;
  • FGCT: Full GC事件所用的总时间(s);
  • GCT: GC事件所用的总时间(s),即YGCT + FGCT;
2.3.7 -compiler输出即时编译器编译过的方法、耗时等信息。
$ jstat -compiler 962 Compiled Failed InvalidTimeFailedType FailedMethod 225364078.341 com/intellij/codeInspection/ex/InspectionProfileImpl addTool

各参数说明如下:
  • Compiled: 编译次数;
  • Failed: 失败的编译次数;
  • Invalid: 无效的编译次数;
  • Time: 编译所消耗的总时间;
  • FailedType: 最后一次编译失败的编译类型;
  • FailedMethod: 最后一次编译失败的类名和方法;
2.3.8 -printcompilation输出已经被即时编译的方法。
$ jstat -printcompilation 962 CompiledSizeType Method 22575231 java/io/ObjectStreamClass$EntryFuture set

各参数说明如下:
  • Compiled: 最近编译的方法执行的编译次数(描述虽然跟-compiler的不同,但实际上看数值它们其实是一样的);
  • Size: 最近编译的方法的字节码的字节数;
  • Type: 最近编译的方法的编译类型;
  • Method: 类名和方法名标识最近编译的方法,其中类名使用/而不是.来分隔,方法名就是类中的方法名,这两项内容的输出格式跟虚拟机参数-XX:+PrintCompilation是一致的;
2.3 jinfo
Java配置信息工具。
jinfo [ option ] pid

主要介绍-flag参数,如果想查看一个已经运行的java程序的虚拟机参数值:
$ jinfo -flag MaxMetaspaceSize 962 -XX:MaxMetaspaceSize=18446744073709547520 $ jinfo -flag MaxTenuringThreshold 962 -XX:MaxTenuringThreshold=6

2.4 jmap
Java内存映像工具。
jmap [ option ] vmid

各参数说明如下:
选项 作用
-dump 生成Java堆转储快照。格式为-dump:[live,],format=b,file=& lt; filename& gt; ,其中live子参数说明是否只dump出存活的对象
-heap 显示Java堆详细信息,如使用哪种回收器、参数配置、分代状况等。只在Linux/Solaris平台下有效
-histo 显示堆中对象统计信息,包括类、实例数量、合计容量
-F 当虚拟机进程对-dump选项没有响应时,可使用这个选项强制生成dump快照。只在Linux/Solaris平台下有效
因为整理该文档时使用的是Mac OS,所以只说明-dump和-histo的使用。
2.4.1 -dump生成Java堆转储快照。
这个最常用,可以将当前java程序的堆内存dump下来,然后再做分析。
$ jmap -dump:format=b,file=idea_heap.hrpof 962 Dumping heap to /private/tmp/idea_heap.hrpof ... Heap dump file created $ ls -lh idea_heap.hrpof -rw-------1 yyhwheel321M25 22:56 idea_heap.hrpof

可以看到也是有一定空间大小的,取决于当前java程序所占用的堆内存空间大小,堆内存dump下来后就可以使用MAT打开进行相应的分析了。
2.4.2 -histo显示堆中对象统计信息,包括类、实例数量、合计容量。
$ jmap -histo 962 | more num#instances#bytesclass name (module) ------------------------------------------------------- 1:82632898417200[B (java.base@11.0.5) 2:53134612752304java.lang.String (java.base@11.0.5) 3:3611812640928[I (java.base@11.0.5) 4:16289710665248[Ljava.lang.Object; (java.base@11.0.5) $ jmap -histo 962 > instances.txt $ wc -l instances.txt 26304 instances.txt

内容也是比较多的,所以可以保存到文件中,然后再进行相应分析。
2.5 jstack
Java堆栈跟踪工具。
jstack [ option ] vimd

用于生成虚拟机当前时刻的线程快照(一般称为threaddump或者javacore文件)。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈集合,生成线程快照的目的通常是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间挂起等,都是导致线程长时间停顿的常见原因。
各参数说明如下:
选项 作用
-F 当正常输出的请求不被响应时,强制输出线程堆栈
-l 除堆栈外,显示关于锁的附加信息(如果有死锁,会分析出来,非常有用)
-m 如果调用到本地方法的话,可以显示C/C++的堆栈
$ jstack -l 962 | more 2020-02-05 23:18:07 Full thread dump OpenJDK 64-Bit Server VM (11.0.5+10-b520.30 mixed mode):Threads class SMR info: _java_thread_list=0x0000600002fdb0e0, length=42, elements= 0x00007f8b55009800, 0x00007f8b5500a800, 0x00007f8b53822800, 0x00007f8b53806000, 0x00007f8b53826000, 0x00007f8b53827000, 0x00007f8b5501f800, 0x00007f8b54009800, 0x00007f8b54003800, 0x00007f8b551c2800, 0x00007f8b53966800, 0x00007f8b54279000, 0x00007f8b53996000, 0x00007f8b5530e000, 0x00007f8b53bbe000, 0x00007f8b5445d800, 0x00007f8b53a01800, 0x00007f8b53a22000, 0x00007f8b53ced800, 0x00007f8b5811c000, 0x00007f8b57bdb000, 0x00007f8b57341000, 0x00007f8b57460000, 0x00007f8b588ee800, 0x00007f8b588f4800, 0x00007f8b541fc800, 0x00007f8b541fd800, 0x00007f8b57826800, 0x00007f8b56dd0000, 0x00007f8b62825800, 0x00007f8b5e932000, 0x00007f8b59872800, 0x00007f8b4407f000, 0x00007f8b443f5000, 0x00007f8b43d76800, 0x00007f8b44d42000, 0x00007f8b62971000, 0x00007f8b610d9000, 0x00007f8b44d48000, 0x00007f8b5f8b8000, 0x00007f8b555ec000, 0x00007f8b4436c800"Reference Handler" #2 daemon prio=10 os_prio=31 cpu=31.02ms elapsed=82877.18s tid=0x00007f8b55009800 nid=0x3803 waiting on condition[0x00007000036e3000] java.lang.Thread.State: RUNNABLE at java.lang.ref.Reference.waitForReferencePendingList(java.base@11.0.5/Native Method) at java.lang.ref.Reference.processPendingReferences(java.base@11.0.5/Reference.java:241) at java.lang.ref.Reference$ReferenceHandler.run(java.base@11.0.5/Reference.java:213)Locked ownable synchronizers: - None $ jstack -l 962 > idea_stack.txt $ wc -l idea_stack.txt 635 idea_stack.txt

2.6 jconsole
Java监视与管理控制台。
在jdk的bin目录是有jconsole的,可以直接打开,在Unix系操作系统上,直接输入jsconsole也可以直接打开。这里主要看一下其功能有哪些,因为实际上对于可视化监控,是更推荐VisualVM,jconsole本身个人用得也不多。
2.6.1 概览
JVM核心参数配置常用调试命令工具与调优思路

文章图片

2.6.2 内存
JVM核心参数配置常用调试命令工具与调优思路

文章图片

2.6.3 线程
JVM核心参数配置常用调试命令工具与调优思路

文章图片

2.6.4 类
JVM核心参数配置常用调试命令工具与调优思路

文章图片

2.6.5 VM概要
JVM核心参数配置常用调试命令工具与调优思路

文章图片

2.6.6 MBean
JVM核心参数配置常用调试命令工具与调优思路

文章图片

2.7 VisualVM
VisualVM是更常用的可视化监控工具,它本身还具有插件扩展功能,因此功能非常强大,目前使用非常多。
需要先说明的是一些安装上的问题,虽然安装好jdk之后是会有一个VisualVM的程序在jdk的bin目录下,但是不建议使用jdk提供的这个,建议直接去官网下载最新的版本使用。
下载:
http://visualvm.github.io/download.html
插件下载:
http://visualvm.github.io/pluginscenters.html
插件的安装这里就不做介绍了,实际上不复杂,可以查找相关资料。
下面会给出其几个比较主要的功能视图的基本情况。
2.7.1 Overview
JVM核心参数配置常用调试命令工具与调优思路

文章图片

2.7.2 Monitor
JVM核心参数配置常用调试命令工具与调优思路

文章图片

2.7.3 Threads
JVM核心参数配置常用调试命令工具与调优思路

文章图片

2.4.7 Visual GC
JVM核心参数配置常用调试命令工具与调优思路

文章图片

2.8 远程监控
2.8.1 JMX远程监控JMX的全称为Java Management Extensions,是管理Java的一种扩展。这种机制可以方便的管理正在运行中的Java程序。常用于管理线程,内存,日志Level,服务重启,系统环境等。
如果希望我们的Java程序被JMX管理,那么可以在启动Java程序时添加下面的参数:
-Dcom.sun.management.jmxremote.port=端口号 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=远程主机ip

之后打开VisualVM,进行下面的操作即可进行远程监控:
  • 1.右键Remote,选择Add Remote Host,填写前面配置的远程主机IP;
  • 2.在Remote的下拉列表中,选择刚刚添加的远程主机,右键,并选择Add JMX Connection,然后在Connection中填写前面配置的端口号即可,之后可以看到远程主机的下拉列表中有JMX监控,双击就可以打开VisualVM的监控界面;
2.8.2 Jstatd远程监控使用jmx可以很好地监控java程序,但其实也有一个问题:原来没有被监控的java程序要想被监控,则需要修改启动参数并重启服务。那有没有更好的办法呢?
可以使用jstat,前面已经介绍过jstat了,而jstatd可以理解为它的服务端,在一台主机上面启动了jstatd,我们就可以监控当前这台主机上面所有的java程序。
先找到tools.jar的路径:
$ pwd /Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/lib $ ls tools.jar tools.jar

创建jstatd启动所需的安全策略文件:
$ cat /tmp/jstatd.all.policy grant codebase "file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/lib/tools.jar" permission java.security.AllPermission; ;

然后启动jstatd
$ jstatd -J-Djava.security.policy=/tmp/jstatd.all.policy -p 34893

打开VisualVM,执行下面的操作:
  • 1.右键Remote,选择Add Remote Host,填写前面配置的远程主机IP;
  • 2.在Remote的下拉列表中,选择刚刚添加的远程主机,右键,并选择Add jstatd Connection,然后在Connection中填写前面配置的端口号即可,之后可以看到远程主机的下拉列表中有很多java进程,双击就可以打开VisualVM的监控界面,并对某一个java进程进行监控;
3 JVM优化这里只是介绍一些比较常规的优化方法和思路,真正在生产环境中进行优化时,还需要看具体的业务,多总结这方面的经验总是有好处的。
3.1 堆内存大小调优
3.1.1 -Xms和-Xmx即初始化堆内存大小和最大堆内存大小。
(1)-Xms
初始化的堆内存大小配置多少比较合适?可以启动你的Java程序,看稳定下来后,堆内存大小是多少,那么其实就可以设定为这个值。不建议设置得太小或者远小于程序稳定后所占的堆内存空间,因为这样会导致程序在启动时会频繁对堆内存空间进行扩容,不断申请内存,这会对启动速度造成一定影响。
也看到有一些程序会把-Xms-Xmx配置成一样大的,这样就完全避免了申请空间的影响,因为可能会存在这样一种情况:
-Xmx配置为2G,程序启动后堆内存稳定在1G左右。但程序本身有可能会承受突然的并发,假设1G的堆内存空间无法处理这些并发,而2G的堆内存空间是可以处理的。 这样可能会导致一个问题,就是并发过来的时候匆忙申请堆内存空间,但实际上请求已经处理不过来了,很有可能 在申请内存空间与处理请求的同一时间,程序就因为堆内存空间不足而OOM了。当然,这只是假设有这样一种情况(实际也遇到过),生产环境上还是考虑你程序的实际场景和实现方式。

(2)-Xmx
它应该是根据你程序的实际需要而进行适当地设置,并不是越大越好的,因为即使当前堆内存大小还没有达到最大堆内存大小,这部分内存也会被JVM虚拟机锁定。
3.2 非堆内存大小调优
3.2.1 -Xss即线程栈大小。
注意这使用的是本地直接内存的空间,线程在执行时占用的主要空间是方法执行时的本地变量表,这也就意味着,当前线程压栈越多,其占用的空间也就越大,所以不能无限制地进行压栈,或者说线程栈所占用的空间不能无限地大,应该要做一定的限制,这时就可以配置-Xss参数。
比较常见的是配置-Xss256k-Xss512k等,当然取决于你的程序,如果确定本身程序不会很复杂,调用栈也不会很深,调小一点可以节省一定的内存空间。
3.2.2 -XX:MaxMetaspaceSize即最大元空间大小。
即用来保存类信息、

    推荐阅读