Java 常用工具详解

2021-08-29 language java

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-exportsJDK9 以上版本才支持。

获取 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 反汇编工具。