Java 安装包中除了 JVM 虚拟机之外,还提供了一些常用的命令行,这里简单介绍常用的命令。
jstat
通过 JVM 内建的指令对 Java 应用的资源和性能进行实时的命令行监控,命令行格式如下。
jstat [-t] <-Command> <PID> [Interval] [Counts]
-class 统计ClassLoader行为信息
-compiler 统计JIT行为信息
-gcutil 统计gc时栈的使用占比
-gc 统计gc时栈的字节信息
会按照 Interval 间隔(毫秒)显示 Counts 次,如果不指定次数则一直执行,也可以通过 -t
显示当前进程运行时间。
----- 统计ClassLoader信息
----- 装载14154个类共31471字节,卸载39个类共45.4字节,耗时5.42秒
$ jstat -class 4022416
Loaded Bytes Unloaded Bytes Time
14154 31471.0 39 45.4 5.42
----- 统计GC时的栈使用占比以及耗时
----- S0 S1 Eden Old MetaSpace 内存空间使用占比,CSS 压缩类占比
----- YGC/YGCT 统计YoungGC次数和耗时 FGC/FGCT 统计FullGC次数和耗时 GCT总耗时
$ jstat -gcutil 4022416 1000 5
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
20.03 0.00 99.47 43.92 98.17 96.03 62 0.375 6 0.141 0.516
20.03 0.00 99.53 43.92 98.17 96.03 62 0.375 6 0.141 0.516
20.03 0.00 99.54 43.92 98.17 96.03 62 0.375 6 0.141 0.516
20.03 0.00 99.54 43.92 98.17 96.03 62 0.375 6 0.141 0.516
20.03 0.00 99.57 43.92 98.17 96.03 62 0.375 6 0.141 0.516
----- 统计GC时的栈使用情况以及耗时
----- S0 S1 Eden Old MetaSpace 信息,其中 C 表示总空间(KB),而 U 表示已经使用空间(KB),耗时与上面相同
$ jstat -gc 4022416 1000 5
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
7024.0 7024.0 3374.4 0.0 36576.0 2746.2 1376.0 211.3 32.0 97131.4 10612.0 10190.3 64 0.380 6 0.141 0.521
7024.0 7024.0 3374.4 0.0 36576.0 2746.2 1376.0 211.3 32.0 97131.4 10612.0 10190.3 64 0.380 6 0.141 0.521
7024.0 7024.0 3374.4 0.0 36576.0 2746.2 1376.0 211.3 32.0 97131.4 10612.0 10190.3 64 0.380 6 0.141 0.521
7024.0 7024.0 3374.4 0.0 36576.0 2865.3 1376.0 211.3 32.0 97131.4 10612.0 10190.3 64 0.380 6 0.141 0.521
7024.0 7024.0 3374.4 0.0 36576.0 3510.8 1376.0 211.3 32.0 97131.4 10612.0 10190.3 64 0.380 6 0.141 0.521
jstack
可以用来观察 JVM 当前所有线程的运行情况和当前状态,也包括了 Core 文件。
$ jstack [options] <PID>
-F 强制输出线程的栈,当负载较高时可以加上该参数
-l 同时显示锁的附加信息
-m 调用本地方法时可以显示 C/C++ 的堆栈
打印的线程包含了 JVM 线程和用户线程两类,一般重点关注用户线程即可。另外,通过 jinfo 可以从 core 文件中知道崩溃 Jave 的配置信息。
jmap
用来查看 JVM 当前栈信息,包括各个对象的数量、占用空间等。
----- 显示类的内存使用情况,包括对象数量、内存大小、完全限定类名 -histo:live
$ jmap -histo 4022416
num #instances #bytes class name
----------------------------------------------
1: 740112 64463272 [C
2: 103817 23393960 [B
3: 739391 17745384 java.lang.String
4: 273711 13138128 com.sun.tools.javac.file.ZipFileIndex$Entry
5: 362864 8708736 com.sun.tools.javac.util.List
6: 38364 3582936 [Ljava.lang.Object;
----- 显示整体堆信息
$ jmap -heap 4022416
Attaching to process ID 4022416, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.265-b01
using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 8589934592 (8192.0MB)
NewSize = 174718976 (166.625MB)
MaxNewSize = 697892864 (665.5625MB)
OldSize = 349569024 (333.375MB)
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 = 157286400 (150.0MB)
used = 41684912 (39.75382995605469MB)
free = 115601488 (110.24617004394531MB)
26.502553304036457% used
Eden Space:
capacity = 139853824 (133.375MB)
used = 39180088 (37.36504364013672MB)
free = 100673736 (96.00995635986328MB)
28.015028033841965% used
From Space:
capacity = 17432576 (16.625MB)
used = 2504824 (2.3887863159179688MB)
free = 14927752 (14.236213684082031MB)
14.368639494243421% used
To Space:
capacity = 17432576 (16.625MB)
used = 0 (0.0MB)
free = 17432576 (16.625MB)
0.0% used
concurrent mark-sweep generation:
capacity = 349569024 (333.375MB)
used = 155050312 (147.86750030517578MB)
free = 194518712 (185.50749969482422MB)
44.35470575333357% used
34582 interned Strings occupying 3705320 bytes.
----- 导出内存文件,可用于问题定位,注意会暂停应用,如果内存很大会有较大耗时
$ jmap -dump 4022416
MAT
Memory Analyzer Tool, MAT 基于 Eclipse 的跨平台内存分析工具,一个快速、功能丰富的 JAVA Heap 分析工具,通过读取转储文件进行分析,可以辅助查找内存泄露、分析内存消耗等,通常用来处理 HPROF
二进制的 Heap Dump 文件。
下载时选择 Stand-alone Eclipse RCP Applications
对应的包,同时需要注意版本,一般 1.11.0
对应了 Java 1.8
版本,否则可能有些参数不支持,例如 --add-exports
在 JDK9
以上版本才支持。
获取 Dump 文件
获取 Heap Dump 文件的方式有很多,通常可以通过工具来获取。
jmap -dump:format=b,file=jmap.info <PID>
可以通过参数配置在特定的条件下触发转储:
- 触发
OOM
时转储,启动时添加JVM
参数-XX:+HeapDumpOnOutOfMemoryError
会自动转储。 FullGC
触发前转储,可以添加-XX:+HeapDumpBeforeFullGC
参数开启。- 通过
jmap
导出,将文件转储到指定目录下。
上述的 -XX:+ARGS
会开启,如果要关闭则使用 -XX:-ARGS
,或者也可以通过 jinfo
直接在线调整,例如通过 jinfo -flag +HeapDumpBeforeFullGC PID
命令开启。
需要注意的时,执行 dump 会暂停服务执行,如果内存很大,那么 dump 文件也会比较大,注意保存的目录。
解析
然后可以通过如下命令解析。
./ParseHeapDump.sh jmap.info \
org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components
除了上述的版本不兼容之外,还有可能出现 java.lang.OutOfMemoryError: Java heap space
错误,此时可以修改 MemoryAnalyzer.ini
配置文件,增加如下内容。
-vmargs
-Xmx16g
-XX:-UseGCOverheadLimit
其它
远程调试
不同 JDK 版本对应的配置略有区别。
----- JDK8
-agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=n
----- JDK11/17
-agentlib:jdwp=transport=dt_socket,address=*:8787,server=y,suspend=n
主要是从 Java9 修改了默认行为,之前的配置会默认监听 0.0.0.0:8787
而之后则默认监听 127.0.0.1:8787
地址。
常用工具
- jd-gui 独立的 Java 反汇编工具。