生产环境如何排除和优化 JVM? https://cloud.tencent.com/developer/article/2177482
问题排查工具有哪些 给一个系统定位问题的时候,知识、经验是关键基础,数据是依据,工具是运用知识处理数据的手段。这里说的数据主要包括以下几种,当运行的程序出现问题的时候,你首先也应该找到相关的数据进行分析。
异常堆栈
虚拟机运行日志
垃圾收集器日志
线程快照(threaddump文件)
堆转存快照(heapdump文件)
如果是在生产环境中直接排查 JVM 的话,最简单的做法就是使用 JDK 自带的 6 个非常实用的命令行工具来排查。它们分别是:jps、jstat、jinfo、jmap、jhat 和 jstack,它们都位于 JDK 的 bin 目录下,可以使用命令行工具直接运行,其目录如下图所示:
1. jps(虚拟机进程状况工具) jps(JVM Process Status tool,虚拟机进程状况工具)它的功能和 Linux 中的 ps 命令比较类似,用于列出正在运行的 JVM 的 LVMID(Local Virtual Machine IDentifier,本地虚拟机唯一 ID),以及 JVM 的执行主类、JVM 启动参数等信息。语法如下:
常用的 options 选项:
-l:用于输出运行主类的全名,如果是 jar 包,则输出 jar 包的路径;
-q:用于输出 LVMID(Local Virtual Machine Identifier,虚拟机唯一 ID);
-m:用于输出虚拟机启动时传递给主类 main() 方法的参数;
-v:用于输出启动时的 JVM 参数。
2. jstat(虚拟机统计信息监视工具)
参考文献https://blog.csdn.net/javalingyu/article/details/124800644
概述 jstat(JVM Statistics Monitoring Tool,虚拟机统计信息监视工具)用于监控虚拟机的运行状态信息。
Jstat名称:Java Virtual Machine statistics monitoring tool
官方文档:https://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstat.html
功能介绍: Jstat是JDK自带的一个轻量级小工具。它位于java的bin目录下,主要利用JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收状况的监控。
使用说明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 C:\Users\Administrator>jstat -help Usage: jstat -help|-options jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]] Definitions: <option> An option reported by the -options option <vmid> Virtual Machine Identifier. A vmid takes the following form: <lvmid>[@<hostname>[:<port>]] Where <lvmid> is the local vm identifier for the target Java virtual machine, typically a process id; <hostname> is the name of the host running the target Java virtual machine; and <port> is the port number for the rmiregistry on the target host. See the jvmstat documentation for a more complete description of the Virtual Machine Identifier. <lines> Number of samples between header lines. <interval> Sampling interval. The following forms are allowed: <n>["ms"|"s"] Where <n> is an integer and the suffix specifies the units as milliseconds("ms") or seconds("s"). The default units are "ms". <count> Number of samples to take before terminating.
参数说明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 option:参数选项 -class 用于查看类加载情况的统计,包括已经加载和未加载的类。 -compiler 用于查看HotSpot中即时编译器编译情况的统计 -gc 用于查看JVM中堆的垃圾收集情况的统计 -gccapacity 用于查看新生代、老生代及持久代的存储容量情况 -gcmetacapacity 显示metaspace的大小 -gcnew 用于查看新生代垃圾收集的情况 -gcnewcapacity 用于查看新生代存储容量的情况 -gcold 用于查看老生代及持久代垃圾收集的情况 -gcoldcapacity 用于查看老生代的容量 -gcutil 显示垃圾收集信息 -gccause 显示垃圾回收的相关信息(通-gcutil),同时显示最后一次仅当前正在发生的垃圾收集的原因 -printcompilation 输出JIT编译的方法信息 -t:可以在打印的列加上Timestamp列,用于显示系统运行的时间 -h:可以在周期性数据输出的时候,指定输出多少行以后输出一次表头 vmid:Virtual Machine ID( 进程的 pid) interval:执行每次的间隔时间,单位为毫秒 count:用于指定输出多少次记录,缺省则会一直打印
1.-class类加载统计 1 2 3 4 5 6 [root@hadoop ~]# jps #先通过jps获取到java进程号(这里是一个zookeeper进程) 3346 QuorumPeerMain 7063 Jps [root@hadoop ~]# jstat -class 3346 #统计JVM中加载的类的数量与size Loaded Bytes Unloaded Bytes Time 1527 2842.7 0 0.0 1.02
Loaded:加载类的数量
Bytes:加载类的size,单位为Byte
Unloaded:卸载类的数目
Bytes:卸载类的size,单位为Byte
Time:加载与卸载类花费的时间
2.-compiler 编译统计 1 2 3 [root@hadoop ~]# jstat -compiler 3346 #用于查看HotSpot中即时编译器编译情况的统计 Compiled Failed Invalid Time FailedType FailedMethod 404 0 0 0.19 0
Compiled:编译任务执行数量
Failed:编译任务执行失败数量
Invalid:编译任务执行失效数量
Time:编译任务消耗时间
FailedType:最后一个编译失败任务的类型
FailedMethod:最后一个编译失败任务所在的类及方法
3.-gc 垃圾回收统计 1 2 3 [root@hadoop ~]# jstat -gc 3346 #用于查看JVM中堆的垃圾收集情况的统计 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 128.0 128.0 0.0 128.0 1024.0 919.8 15104.0 2042.4 8448.0 8130.4 1024.0 996.0 7 0.019 0 0.000 0.019
S0C:年轻代中第一个survivor(幸存区)的容量 (字节)
S1C:年轻代中第二个survivor(幸存区)的容量 (字节)
S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节)
S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节)
EC:年轻代中Eden(伊甸园)的容量 (字节)
EU:年轻代中Eden(伊甸园)目前已使用空间 (字节)
OC:Old代的容量 (字节)
OU:Old代目前已使用空间 (字节)
MC:metaspace(元空间)的容量 (字节)
MU:metaspace(元空间)目前已使用空间 (字节)
CCSC:当前压缩类空间的容量 (字节)
CCSU:当前压缩类空间目前已使用空间 (字节)
YGC:从应用程序启动到采样时年轻代中gc次数
YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
FGC:从应用程序启动到采样时old代(全gc)gc次数
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)
4.-gccapacity 堆内存统计 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@hadoop ~]# jstat -gccapacity 3346 #用于查看新生代、老生代及持久代的存储容量情况 NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC 1280.0 83264.0 1280.0 128.0 128.0 1024.0 15104.0 166592.0 15104.0 15104.0 0.0 1056768.0 8448.0 0.0 1048576.0 1024.0 7 0 [root@hadoop ~]# jstat -gccapacity -h5 3346 1000 #-h5:每5行显示一次表头 1000:每1秒钟显示一次,单位为毫秒 NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC 1280.0 83264.0 1280.0 128.0 128.0 1024.0 15104.0 166592.0 15104.0 15104.0 0.0 1056768.0 8448.0 0.0 1048576.0 1024.0 7 0 1280.0 83264.0 1280.0 128.0 128.0 1024.0 15104.0 166592.0 15104.0 15104.0 0.0 1056768.0 8448.0 0.0 1048576.0 1024.0 7 0 1280.0 83264.0 1280.0 128.0 128.0 1024.0 15104.0 166592.0 15104.0 15104.0 0.0 1056768.0 8448.0 0.0 1048576.0 1024.0 7 0 1280.0 83264.0 1280.0 128.0 128.0 1024.0 15104.0 166592.0 15104.0 15104.0 0.0 1056768.0 8448.0 0.0 1048576.0 1024.0 7 0 1280.0 83264.0 1280.0 128.0 128.0 1024.0 15104.0 166592.0 15104.0 15104.0 0.0 1056768.0 8448.0 0.0 1048576.0 1024.0 7 0 NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC 1280.0 83264.0 1280.0 128.0 128.0 1024.0 15104.0 166592.0 15104.0 15104.0 0.0 1056768.0 8448.0 0.0 1048576.0 1024.0 7 0 1280.0 83264.0 1280.0 128.0 128.0 1024.0 15104.0 166592.0 15104.0 15104.0 0.0 1056768.0 8448.0 0.0 1048576.0 1024.0 7 0 1280.0 83264.0 1280.0 128.0 128.0 1024.0 15104.0 166592.0 15104.0 15104.0 0.0 1056768.0 8448.0 0.0 1048576.0 1024.0 7 0 1280.0 83264.0 1280.0 128.0 128.0 1024.0 15104.0 166592.0 15104.0 15104.0 0.0 1056768.0 8448.0 0.0 1048576.0 1024.0 7 0
NGCMN:年轻代(young)中初始化(最小)的大小(字节)
NGCMX:年轻代(young)的最大容量 (字节)
NGC:年轻代(young)中当前的容量 (字节)
S0C:年轻代中第一个survivor(幸存区)的容量 (字节)
S1C:年轻代中第二个survivor(幸存区)的容量 (字节)
EC:年轻代中Eden(伊甸园)的容量 (字节)
OGCMN:old代中初始化(最小)的大小 (字节)
OGCMX:old代的最大容量(字节)
OGC:old代当前新生成的容量 (字节)
OC:Old代的容量 (字节)
MCMN:metaspace(元空间)中初始化(最小)的大小 (字节)
MCMX:metaspace(元空间)的最大容量 (字节)
MC:metaspace(元空间)当前新生成的容量 (字节)
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:从应用程序启动到采样时年轻代中gc次数
FGC:从应用程序启动到采样时old代(全gc)gc次数
1 2 3 [root@hadoop ~]# jstat -gcmetacapacity 3346 #显示元数据空间的大小 MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC FGCT GCT 0.0 1056768.0 8448.0 0.0 1048576.0 1024.0 8 0 0.000 0.020
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:从应用程序启动到采样时年轻代中gc次数
FGC:从应用程序启动到采样时old代(全gc)gc次数
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)
6.-gcnew 新生代垃圾回收统计 1 2 3 [root@hadoop ~]# jstat -gcnew 3346 #用于查看新生代垃圾收集的情况 S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT 128.0 128.0 67.8 0.0 1 15 64.0 1024.0 362.2 8 0.020
S0C:年轻代中第一个survivor(幸存区)的容量 (字节)
S1C:年轻代中第二个survivor(幸存区)的容量 (字节)
S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节)
S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节)
TT:持有次数限制
MTT:最大持有次数限制
DSS:期望的幸存区大小
EC:年轻代中Eden(伊甸园)的容量 (字节)
EU:年轻代中Eden(伊甸园)目前已使用空间 (字节)
YGC:从应用程序启动到采样时年轻代中gc次数
YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
7.-gcnewcapacity 新生代内存统计 1 2 3 [root@hadoop ~]# jstat -gcnewcapacity 3346 #用于查看新生代存储容量的情况 NGCMN NGCMX NGC S0CMX S0C S1CMX S1C ECMX EC YGC FGC 1280.0 83264.0 1280.0 8320.0 128.0 8320.0 128.0 66624.0 1024.0 8 0
NGCMN:年轻代(young)中初始化(最小)的大小(字节)
NGCMX:年轻代(young)的最大容量 (字节)
NGC:年轻代(young)中当前的容量 (字节)
S0CMX:年轻代中第一个survivor(幸存区)的最大容量 (字节)
S0C:年轻代中第一个survivor(幸存区)的容量 (字节)
S1CMX:年轻代中第二个survivor(幸存区)的最大容量 (字节)
S1C:年轻代中第二个survivor(幸存区)的容量 (字节)
ECMX:年轻代中Eden(伊甸园)的最大容量 (字节)
EC:年轻代中Eden(伊甸园)的容量 (字节)
YGC:从应用程序启动到采样时年轻代中gc次数
FGC:从应用程序启动到采样时old代(全gc)gc次数
8.-gcold 老年代垃圾回收统计 1 2 3 [root@hadoop ~]# jstat -gcold 3346 #用于查看老年代及持久代垃圾收集的情况 MC MU CCSC CCSU OC OU YGC FGC FGCT GCT 8448.0 8227.5 1024.0 1003.7 15104.0 2102.2 8 0 0.000 0.020
MC:metaspace(元空间)的容量 (字节)
MU:metaspace(元空间)目前已使用空间 (字节)
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
OC:Old代的容量 (字节)
OU:Old代目前已使用空间 (字节)
YGC:从应用程序启动到采样时年轻代中gc次数
FGC:从应用程序启动到采样时old代(全gc)gc次数
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)
9.-gcoldcapacity 老年代内存统计 1 2 3 [root@hadoop ~]# jstat -gcoldcapacity 3346 #用于查看老年代的容量 OGCMN OGCMX OGC OC YGC FGC FGCT GCT 15104.0 166592.0 15104.0 15104.0 8 0 0.000 0.020
OGCMN:old代中初始化(最小)的大小 (字节)
OGCMX:old代的最大容量(字节)
OGC:old代当前新生成的容量 (字节)
OC:Old代的容量 (字节)
YGC:从应用程序启动到采样时年轻代中gc次数
FGC:从应用程序启动到采样时old代(全gc)gc次数
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)
10.-gcutil 垃圾回收统计 1 2 3 [root@hadoop ~]# jstat -gcutil 3346 #显示垃圾收集信息 S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 52.97 0.00 42.10 13.92 97.39 98.02 8 0.020 0 0.000 0.020
S0:年轻代中第一个survivor(幸存区)已使用的占当前容量百分比
S1:年轻代中第二个survivor(幸存区)已使用的占当前容量百分比
E:年轻代中Eden(伊甸园)已使用的占当前容量百分比
O:old代已使用的占当前容量百分比
M:元数据区已使用的占当前容量百分比
CCS:压缩类空间已使用的占当前容量百分比
YGC :从应用程序启动到采样时年轻代中gc次数
YGCT :从应用程序启动到采样时年轻代中gc所用时间(s)
FGC :从应用程序启动到采样时old代(全gc)gc次数
FGCT :从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)
11.-gccause 1 2 3 [root@hadoop ~]# jstat -gccause 3346 #显示垃圾回收的相关信息(通-gcutil),同时显示最后一次或当前正在发生的垃圾回收的诱因 S0 S1 E O M CCS YGC YGCT FGC FGCT GCT LGCC GCC 52.97 0.00 46.09 13.92 97.39 98.02 8 0.020 0 0.000 0.020 Allocation Failure No GC
LGCC:最后一次GC原因
GCC:当前GC原因(No GC 为当前没有执行GC)
12.-printcompilation JVM编译方法统计 1 2 3 [root@hadoop ~]# jstat -printcompilation 3346 #输出JIT编译的方法信息 Compiled Size Type Method 421 60 1 sun/nio/ch/Util$2 clear
Compiled:编译任务的数目
Size:方法生成的字节码的大小
Type:编译类型
Method:类名和方法名用来标识编译的方法。类名使用/做为一个命名空间分隔符。方法名是给定类中的方法。上述格式是由-XX:+PrintComplation选项进行设置的
远程监控 与jps一样,jstat也支持远程监控,同样也需要开启安全授权,方法参照jps。
1 2 3 4 5 6 C:\Users\Administrator>jps 192.168.146.128 3346 QuorumPeerMain 3475 Jstatd C:\Users\Administrator>jstat -gcutil 3346@192.168.146.128 S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 52.97 0.00 65.15 13.92 97.39 98.02 8 0.020 0 0.000 0.020
3. jinfo(查询虚拟机参数配置工具) jinfo(Configuration Info for Java)用于查看和调整虚拟机各项参数。语法如下:
查看 JVM 参数示例如下:
1 2 3 ➜ jinfo -flags 45129 VM Flags: -XX:CICompilerCount=3 -XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:MaxNewSize=1431306240 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=89128960 -XX:OldSize=179306496 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
其中 45129 是使用 jps 查询的 LVMID。
我们可以通过 jinfo -flag [+/-]name 来修改虚拟机的参数值,比如下面的示例:
1 2 3 4 5 6 7 8 ➜ jinfo -flag PrintGC 45129 -XX:-PrintGC ➜ jinfo -flag +PrintGC 45129 ➜ jinfo -flag PrintGC 45129 -XX:+PrintGC ➜ jinfo -flag -PrintGC 45129 ➜ jinfo -flag PrintGC 45129 -XX:-PrintGC
4. jmap(堆快照生成工具) jmap(Memory Map for Java)用于查询堆的快照信息。
jmap -heap 查询堆信息示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 ➜ jmap -heap 45129 Attaching to process ID 45129, please wait ... Debugger attached successfully. Server compiler detected. JVM version is 25.101-b13 using thread-local object allocation. Parallel GC with 6 thread(s) Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 4294967296 (4096.0MB) NewSize = 89128960 (85.0MB) MaxNewSize = 1431306240 (1365.0MB) OldSize = 179306496 (171.0MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB) Heap Usage: PS Young Generation Eden Space: capacity = 67108864 (64.0MB) used = 5369232 (5.1204986572265625MB) free = 61739632 (58.87950134277344MB) 8.000779151916504% used From Space: capacity = 11010048 (10.5MB) used = 0 (0.0MB) free = 11010048 (10.5MB) 0.0% used To Space: capacity = 11010048 (10.5MB) used = 0 (0.0MB) free = 11010048 (10.5MB) 0.0% used PS Old Generation capacity = 179306496 (171.0MB) used = 0 (0.0MB) free = 179306496 (171.0MB) 0.0% used 2158 interned Strings occupying 152472 bytes.
我们也可以直接生成堆快照文件,示例如下:
1 2 3 ➜ jmap -dump:format=b,file=/Users/admin/Documents/2020.dump 47380 Dumping heap to /Users/admin/Documents/2020.dump ... Heap dump file created
jmap -histo
jmap -histo 命令用于显示 Java 进程中的对象统计信息。该命令将会输出一份对象统计报告,其中包含了对象数量、对象占用的内存大小、对象类名等信息。以下是 jmap -histo 命令输出的结果说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 num #instances #bytes class name ---------------------------------------------- 1: 10002 10002000 [B 2: 1000 900000 [Ljava.lang.Object; 3: 100 200000 [C 4: 2 1200104 [I 5: 1020 53040 java.lang.String 6: 5 32712 java.lang.Object 7: 384 20736 java.lang.Class 8: 1 15664 [Ljava.util.HashMap$Node; 9: 102 7344 java.util.HashMap$Node 10: 10 4000 [Ljava.util.Hashtable$Entry; 11: 101 3636 java.util.Hashtable$Entry 12: 22 3432 java.lang.ref.SoftReference 13: 101 2424 java.util.HashMap 14: 22 1408 java.util.concurrent.ConcurrentHashMap$Node 15: 22 1320 java.lang.ref.WeakReference 16: 22 528 java.util.concurrent.ConcurrentHashMap 17: 11 440 [Ljava.lang.String; 18: 23 368 java.util.concurrent.ConcurrentHashMap$TreeBin 19: 11 352 java.io.ObjectStreamClass$WeakClassKey 20: 2 320 [Ljava.util.concurrent.ConcurrentHashMap$Node; ...
其中,每一行包含了以下信息:
num:对象的序号。
#instances:对象的数量。
#bytes:对象占用的内存大小,单位为字节。
class name:对象的类名。这个列表按照对象占用的内存从大到小进行排序。在这个示例中,对象的数量、类型和大小都是不同的。其中,[B 表示字节数组,[Ljava.lang.Object; 表示对象数组,[C 表示字符数组,[I 表示整数数组,java.lang.String 和其他对象类型都是 Java 标准库中的类。
使用 jmap -histo 命令可以帮助您了解 Java 进程中哪些对象占用了大量的内存,从而更好地进行内存分析和优化。
jmap 的使用教程:
查看进程 ID 首先,您需要查看 Java 应用程序的进程 ID(PID)。您可以使用 jps 命令来查看运行的 Java 进程,例如:
这将显示当前系统中所有运行的 Java 进程的 PID 和类名。
生成堆转储快照 使用 jmap 命令来生成 JVM 的堆转储快照。例如,要生成 PID 为 1234 的 Java 进程的堆转储快照,您可以使用以下命令:
1 jmap -dump:file=heapdump.bin 1234
这将生成一个名为 heapdump.bin 的二进制文件,其中包含了 Java 进程的堆转储快照。
分析堆转储快照 使用 jhat 和 jvisualvm 命令来分析堆转储快照。jhat 命令用于启动一个基于浏览器的分析器,jvisualvm 命令用于启动一个图形用户界面(GUI)分析器。
例如,要使用 jhat 命令启动分析器,您可以使用以下命令:
这将启动一个 Web 服务器,您可以使用浏览器访问 URL http://localhost:7000/ 来查看分析结果。
要使用 jvisualvm 命令启动分析器,您可以使用以下命令:
这将启动一个 GUI 分析器,在界面中打开堆转储快照文件进行分析。
jmap的参数和含义 jmap 是 Java 自带的一个命令行工具,可以生成 JVM 的堆转储快照,并用于分析和诊断 Java 应用程序的内存使用情况。以下是 jmap 命令的各个参数和结果的含义说明:
-dump 生成 JVM 的堆转储快照。
1 jmap -dump:file=<filename> <PID>
参数: file:指定输出文件的名称,可以包含路径。 PID:Java 进程的进程 ID。
结果: 生成一个名为 filename 的二进制文件,其中包含了 Java 进程的堆转储快照。
-heap 显示 Java 进程的堆信息。
参数: PID:Java 进程的进程 ID。 结果: 显示 Java 进程的堆信息,包括堆内存容量、已使用的堆内存、最大堆内存、新生代和老年代的容量等。
-histo 显示 Java 进程中的对象统计信息。
1 jmap -histo[:live] <PID>
参数: PID:Java 进程的进程 ID。 live:指定只显示当前处于活动状态的对象。 结果: 显示 Java 进程中的对象统计信息,包括对象数量、对象占用的内存大小、对象类名等。
-finalizerinfo 显示 Java 进程中的 finalize 队列信息。
1 jmap -finalizerinfo <PID>
参数: PID:Java 进程的进程 ID。 结果: 显示 Java 进程中的 finalize 队列信息,包括队列中对象的数量、finalize 线程的数量等。
-permstat 显示 Java 进程中的永久代信息。
参数: PID:Java 进程的进程 ID。 结果: 显示 Java 进程中的永久代信息,包括永久代内存容量、已使用的永久代内存等。
-F 在无法通过正常通道生成堆转储快照时,强制生成堆转储快照。
1 jmap -dump:file=<filename> -F <PID>
参数: file:指定输出文件的名称,可以包含路径。 PID:Java 进程的进程 ID。 结果: 生成一个名为 filename 的二进制文件,其中包含了 Java 进程的堆转储快照。
总之,jmap 命令提供了多个参数,可以用于分析和诊断 Java 应用程序的内存使用情况。不同的参数可以生成不同的输出结果,您可以根据需要选择合适的参数来使用 jmap 命令。
5. jhat(堆快照分析功能) jhat(JVM Heap Analysis Tool,堆快照分析工具)和 jmap 搭配使用,用于启动一个 web 站点来分析 jmap 生成的快照文件。
执行示例如下:
1 2 3 4 5 6 7 8 9 10 jhat /Users/admin/Documents/2020.dump Reading from /Users/admin/Documents/2020.dump... Dump file created Tue May 26 16:12:41 CST 2020 Snapshot read , resolving... Resolving 17797 objects... Chasing references, expect 3 dots... Eliminating duplicate references... Snapshot resolved. Started HTTP server on port 7000 Server is ready.
上述信息表示 jhat 启动了一个 http 的服务器端口为 7000 的站点来展示信息,此时我们在浏览器中输入:http://localhost:7000/,会看到如下图所示的信息:
6. jstack(查询虚拟机当前的线程快照信息) jstack(Stack Trace for Java)用于查看当前虚拟机的线程快照,用它可以排查线程的执行状况,例如排查死锁、死循环等问题。
比如,我们先写一段死锁的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class NativeOptimize { private static Object obj1 = new Object (); private static Object obj2 = new Object (); public static void main (String[] args) { new Thread (new Runnable () { @Override public void run () { synchronized (obj2) { System.out.println(Thread.currentThread().getName() + "锁住 obj2" ); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj1) { System.out.println("1秒钟后," + Thread.currentThread().getName() + "锁住 obj1" ); } } } }).start(); synchronized (obj1) { System.out.println(Thread.currentThread().getName() + "锁住 obj1" ); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj2) { System.out.println("1秒钟后," + Thread.currentThread().getName() + "锁住 obj2" ); } } } }
以上程序的执行结果如下:
1 2 main:锁住 obj1 Thread-0:锁住 obj2
此时我们使用 jstack 工具打印一下当前线程的快照信息,结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 ➜ bin jstack -l 50016 2020-05-26 18:01:41 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.101-b13 mixed mode): "Attach Listener" java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None "Thread-0" java.lang.Thread.State: BLOCKED (on object monitor) at com.example.optimize.NativeOptimize$1 .run(NativeOptimize.java:25) - waiting to lock <0x000000076abb62d0> (a java.lang.Object) - locked <0x000000076abb62e0> (a java.lang.Object) at java.lang.Thread.run(Thread.java:745) Locked ownable synchronizers: - None "Service Thread" java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None "C1 CompilerThread2" java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None "C2 CompilerThread1" java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None "C2 CompilerThread0" java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None "Signal Dispatcher" java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None "Finalizer" java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait (Native Method) - waiting on <0x000000076ab08ee0> (a java.lang.ref.ReferenceQueue$Lock ) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143) - locked <0x000000076ab08ee0> (a java.lang.ref.ReferenceQueue$Lock ) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164) at java.lang.ref.Finalizer$FinalizerThread .run(Finalizer.java:209) Locked ownable synchronizers: - None "Reference Handler" java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait (Native Method) - waiting on <0x000000076ab06b50> (a java.lang.ref.Reference$Lock ) at java.lang.Object.wait (Object.java:502) at java.lang.ref.Reference.tryHandlePending(Reference.java:191) - locked <0x000000076ab06b50> (a java.lang.ref.Reference$Lock ) at java.lang.ref.Reference$ReferenceHandler .run(Reference.java:153) Locked ownable synchronizers: - None "main" java.lang.Thread.State: BLOCKED (on object monitor) at com.example.optimize.NativeOptimize.main(NativeOptimize.java:41) - waiting to lock <0x000000076abb62e0> (a java.lang.Object) - locked <0x000000076abb62d0> (a java.lang.Object) Locked ownable synchronizers: - None "VM Thread" os_prio=31 tid=0x00007f8c01008800 nid=0x2e03 runnable"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007f8c00803000 nid=0x2007 runnable"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007f8c00006800 nid=0x2403 runnable"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007f8c01800800 nid=0x2303 runnable"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007f8c01801800 nid=0x2a03 runnable"GC task thread#4 (ParallelGC)" os_prio=31 tid=0x00007f8c01802000 nid=0x5403 runnable"GC task thread#5 (ParallelGC)" os_prio=31 tid=0x00007f8c01006800 nid=0x2d03 runnable"VM Periodic Task Thread" os_prio=31 tid=0x00007f8c00010800 nid=0x3803 waiting on conditionJNI global references: 6 Found one Java-level deadlock: ============================= "Thread-0" : waiting to lock monitor 0x00007f8c000102a8 (object 0x000000076abb62d0, a java.lang.Object), which is held by "main" "main" : waiting to lock monitor 0x00007f8c0000ed58 (object 0x000000076abb62e0, a java.lang.Object), which is held by "Thread-0" Java stack information for the threads listed above: =================================================== "Thread-0" : at com.example.optimize.NativeOptimize$1 .run(NativeOptimize.java:25) - waiting to lock <0x000000076abb62d0> (a java.lang.Object) - locked <0x000000076abb62e0> (a java.lang.Object) at java.lang.Thread.run(Thread.java:745) "main" : at com.example.optimize.NativeOptimize.main(NativeOptimize.java:41) - waiting to lock <0x000000076abb62e0> (a java.lang.Object) - locked <0x000000076abb62d0> (a java.lang.Object) Found 1 deadlock.
从上述信息可以看出使用 jstack ,可以很方便地排查出代码中出现“deadlock”(死锁)的问题。
补充jcmd 用来分析java进程的内存占用
添加参数
1 -XX:NativeMemoryTracking=summary
执行命令
1 jcmd 6948 VM.native_memory
返回结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 ➜ ~ jcmd 6948 VM.native_memory 6948: Native Memory Tracking: Total: reserved=6083099KB, committed=1964315KB - Java Heap (reserved=4194304KB, committed=1337344KB) (mmap: reserved=4194304KB, committed=1337344KB) - Class (reserved=1167036KB, committed=133476KB) (classes #21170) (malloc=14012KB #34318) (mmap: reserved=1153024KB, committed=119464KB) - Thread (reserved=201622KB, committed=201622KB) (thread #197) (stack: reserved=200704KB, committed=200704KB) (malloc=624KB #992) (arena=295KB #389) - Code (reserved=257311KB, committed=39431KB) (malloc=7711KB #16415) (mmap: reserved=249600KB, committed=31720KB) - GC (reserved=165959KB, committed=155575KB) (malloc=12715KB #865) (mmap: reserved=153244KB, committed=142860KB) - Compiler (reserved=172KB, committed=172KB) (malloc=41KB #1192) (arena=131KB #7) - Internal (reserved=61345KB, committed=61345KB) (malloc=61313KB #29568) (mmap: reserved=32KB, committed=32KB) - Symbol (reserved=29660KB, committed=29660KB) (malloc=25752KB #266545) (arena=3908KB #1) - Native Memory Tracking (reserved=5508KB, committed=5508KB) (malloc=28KB #315) (tracking overhead=5480KB) - Arena Chunk (reserved=181KB, committed=181KB) (malloc=181KB)
考点分析 Java 虚拟机的排查工具是一个合格程序员必备的技能,使用它我们可以很方便地定位出问题的所在,尤其在团队合作的今天,每个人各守一摊很容易出现隐藏的 bug(缺陷)。因此使用这些排查功能可以帮我们快速地定位并解决问题,所以它也是面试中常问的问题之一。
和此知识点相关的面试题还有以下这些:
除了比较实用的命令行工具之外,有没有方便一点的排查工具?
JVM 常见的调优手段有哪些?
知识扩展 可视化排查工具 JVM 除了上面的 6 个基础命令行工具之外,还有两个重要的视图调试工具,即 JConsole 和 JVisualVM,它们相比于命令行工具使用更方便、操作更简单、结果展现也更直观。
JConsole 和 JVisualVM 都位于 JDK 的 bin 目录下,JConsole(Java Monitoring and Management Console)是最早期的视图调试工具,其启动页面如下图所示:
可以看出我们可以用它来连接远程的服务器,或者是直接调试本机,这样就可以在不消耗生产环境的性能下,从本机启动 JConsole 来连接服务器。
JVisualVM 的启动图如下图所示:
由上图可知,JVisualVM 既可以调试本地也可以调试远程服务器。
JVM 调优 JVM 调优主要是根据实际的硬件配置信息重新设置 JVM 参数来进行调优的,例如,硬件的内存配置很高,但 JVM 因为是默认参数,所以最大内存和初始化堆内存很小,这样就不能更好地利用本地的硬件优势了。因此,需要调整这些参数,让 JVM 在固定的配置下发挥最大的价值。
JVM 常见调优参数包含以下这些:
-Xmx,设置最大堆内存大小;
-Xms,设置初始堆内存大小;
-XX:MaxNewSize,设置新生代的最大内存;
-XX:MaxTenuringThreshold,设置新生代对象经过一定的次数晋升到老生代;
-XX:PretrnureSizeThreshold,设置大对象的值,超过这个值的对象会直接进入老生代;
-XX:NewRatio,设置分代垃圾回收器新生代和老生代内存占比;
-XX:SurvivorRatio,设置新生代 Eden、Form Survivor、To Survivor 占比。
我们要根据自己的业务场景和硬件配置来设置这些值。例如,当我们的业务场景会有很多大的临时对象产生时,因为这些大对象只有很短的生命周期,因此需要把“-XX:MaxNewSize”的值设置的尽量大一些,否则就会造成大量短生命周期的大对象进入老生代,从而很快消耗掉了老生代的内存,这样就会频繁地触发 full gc,从而影响了业务的正常运行。
小结 本课时我们讲了 JVM 排查的 6 个基本命令行工具:jps、jstat、jinfo、jmap、jhat、jstack,以及 2 个视图排查工具:JConsole 和 JVisualVM;同时还讲了 JVM 的常见调优参数。