JVM虚拟机(1):JVM 监控调优-自带命令

JDK 中自带了一些非常实用的 JVM 性能分析和故障监控的小工具。

也有一些第三方分析工具,如 Arthas。基于开发工具的 JVM 分析工具,如 IDEA 的 VisualVM 插件,Eclipse 的 MAT(Memory Analyzer Tool)。

了解 JVM 内存和性能分析工具及使用,对快速判断和定位线上问题是非常有帮助的。

生产或开发环境中可能会碰到以下问题:

  • OOM(OutOfMemoryError):内存溢出(内存不足),没有足够的内存来满足使用,存不下就报错。
  • 内存泄漏(Memory Leak):已申请的内存无法释放,GC无法回收就无法再使用,就造成内浪费,频繁的出现就会造成内存泄漏堆积,耗光内存,就可能导致频繁发生 Full GC,就会导致 CPU 使用率过高。
  • 线程死锁
  • 锁争用(Lock Contention)
  • Java 进程消耗过高的 CPU
  • ……

这些问题不应该被忽视,而是要深究问题根源,真正地解决问题,而不是简单的重启服务或调大内存。

总览

JDK 自带的命令其实和 Linux 相关的命令很像,个人认为是基于 Linux 命令进行的二次包装,命令名在 Linux 命令增加前缀 j,例如, ps -> jpsstat -> jstatinfo -> jinfo 等等。

  • jps:可以查看 Java 进程和 JVM 相关参数信息。
  • jstat:显示已检测到的 JVM 的性能统计信息,包括类加载,内存,垃圾收集等。
  • jstack:打印 Java 进程的线程堆栈信息。将打印完整的类名,方法名,字节码索引和行号。
  • jinfo:打印 Java 进程的配置信息。
  • jmap:打印进程的共享对象内存映射或堆内存详细信息。
  • jhat:创建一个用于分析 Java Heap Dump 文件的 HTTP 服务器。
  • jstatd:监控 JVM 和 开启 JVM 的远程监控服务,是一个 RMI 服务。
  • jcmd:是个多功能功工具,可以用于导出堆,查看 Java 进程,导出线程,执行 GC 等。
  • hprof :不是独立的监控工具,只是一个 Java agent 工具。可用于监控 Java 应用在运行时的 CPU 信息和堆信息。

命令

JDK 自带的分析工具令在安装 JDK 的 bin 中,如: /java/jdk1.8.0_202/bin

所有命令可通过查看命令帮助或命令详情来了解使用,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
命令帮助
# ${java_command} -h
# ${java_command} -help

命令详情
# man ${java_command}

以下示例
[root@iZm5egvez00kmy7eatnnt1Z ~]# jps -h
illegal argument: -h
usage: jps [-help]
jps [-q] [-mlvV] [<hostid>]

Definitions:
<hostid>: <hostname>[:<port>]
[root@iZm5egvez00kmy7eatnnt1Z ~]# jps -help
usage: jps [-help]
jps [-q] [-mlvV] [<hostid>]

Definitions:
<hostid>: <hostname>[:<port>]
[root@iZm5egvez00kmy7eatnnt1Z ~]# man jps

jps

jps:列出已检测到的 Java HotSpot VM(JVM)的信息,可以查看启动类,传入参数和 JVM 参数等信息,类似于 Linux 的 ps -ef|grep java

Java 程序在启动后,会在 java.io.tmpdir指定的目录下(通常是临时文件夹)里,生成一个类似于 hsperfdata_root 的文件夹,里面有以 Java 进程 ID 号命令的文件,进程信息就需要解析文件。在 Linux 中的目录为 /tmp/hsperfdata_${username}

语法

  • 语法:jps [ options ] [ hostid ]

    未指定 hostid 则表示查看本机

  • hostid 格式:[protocol:][[//]hostname][:port][/servername]

    查看远程服务 ,需要在远程服务上有足够的权限来启动 jstatd服务,实际是开启一个 RMI 服务。

  • 输出格式:processId [ [ classname | JARfilename | "Unknown"] [ arg* ] [ jvmarg* ] ]

参数

参数说明

  • -q:只输出 Java 进程 ID
  • -m:输出传入 main 方法的参数。嵌入式 JVM 可能输出 null
  • -l:输出主类的完全包名 或 Jar 包的完全路径名
  • -v:输出传给 JVM 的参数
  • -V:只输出 Java 进程ID 和 类名,jps 不带参数的默认项
  • -Joption:给 JVM 传递参数。例如,J-Xms48m 是将启动内存设置为 48M。注意,option 指参数项。

远程连接

  1. 前提是使用 jstatd 命令启动 RMI 服务。
  2. 指定协议,IP 和端口: jps rmi://172.31.97.209:1099 , 1099 是默认端口。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# jps
18027 Java2Demo.JAR
18032 jps
18005 jstat

# jps -l remote.domain //远程主机
3002 /opt/jdk1.7.0/demo/jfc/Java2D/Java2Demo.JAR
2857 sun.tools.jstatd.jstatd

# jps -m remote.domain:2002 //远程主机,指定端口
3002 /opt/jdk1.7.0/demo/jfc/Java2D/Java2Demo.JAR
3102 sun.tools.jstatd.jstatd -p 2002

# jps -l -m
3526 org.apache.catalina.startup.Bootstrap start
27766 sun.tools.jps.Jps -l -m
31469 /usr/local/activemq-5.15.9//bin/activemq.jar start

jstat

jstat:监视 Java 虚拟机(JVM)统计信息。显示已检测到的 Java HotSpot VM 的性能统计信息。包括类加载,内存,垃圾收集等。

语法

  • 语法:jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

    jstat [ generalOption | outputOptions vmid [ interval[s|ms] [ count ] ]

    option:参数选项,有两种类型,通用选项统计选项

    -t:显示一个时间戳列(Timestamp),单位:秒。

    vmid:JVM 进程 ID。可以是远程服务的 JVM 进程 ID。

    interval:输出时间间隔,单位默认毫秒(ms),可为秒(s),必为正整数。

    count:输出次数,必须为正整数。

    jstat 显示输出格式是一个表格,每个统计项有不同的列名。

参数

  • 通用选项:# jstat -options,列出所有统计选项,见下。

  • 统计选项

    options:输出项,决定输出那些项的统计信息。

    选项 Desc
    -class 显示类加载的统计信息
    -compiler 显示 JVM JIT 编译的统计信息
    -gc 显示 GC 时堆内存的统计信息
    -gccapacity 显示 GC 时堆内存中不同年代(new,old,metaspace)的统计信息
    -gccause 显示引起 GC 事件的摘要信息(与 -gcutil相同)
    -gcutil 显示 GC 时的摘要信息,以使用空间的百分比表示
    -gcmetacapacity 显示 GC 时metaspace(元空间)的内存统计
    -gcnew 显示 GC 时新生代的统计
    -gcnewcapacity 显示 GC 时新生代容量的统计
    -gcold 显示 GC 时老年代的统计
    -gcoldcapacity 显示 GC 时老年代内存统计
    -printcompilation 显示 JVM 编译方法统计信息
  • -class:类加载

    • Loaded:已加载 class 的数量。
    • Bytes:已加载的 class 字节大小。
    • Unloaded:未加载 class 数量。
    • Bytes:未加载的 class 字节大小。
    • Time:class 加载的耗时。
  • -compiler:JIT 编译

    • Compiled:执行编译任务的数量。
    • Failed:编译失败的数量。
    • Invalid:编译无效的数量。
    • Time:执行编译的耗时。
    • FailedType:最近一次编译失败的编译类型。
    • FailedMethod:最近一次编译失败的类名和方法。
  • -gc:垃圾回收时堆内存信息

    • S0C:第一个辛存者区容量 (kB).

    • S1C:第二个辛存者区容量 (kB).

    • S0U:第一个辛存区的使用大小 (kB).

    • S1U:第二个辛存区的使用大小 (kB).

    • EC:当前 Eden 区容量 (kB).

    • EU:当前 Eden 区使用大小 (kB).

    • OC:当前老年代容量(KB)

    • OU:老年代的使用大小(KB)

    • MC:元空间大小 (kB).

    • MU:元空间的使用大小 (kB).

    • CCSC:压缩类空间大小 (kB).

    • CCSU:压缩类空间的使用大小 (kB).

    • YGC:年轻代垃圾回收次数.

    • YGCT:年轻代垃圾回收消耗时间.

    • FGC:老年代垃圾回收次数.

    • FGCT:老年代垃圾回收消耗时间.

    • GCT:垃圾回收总消耗时间.

  • -gccapacity:年代空间和容间

    • NGCMN: 新生代最小容量 (kB).
    • NGCMX: 新生代最大容量 (kB).
    • NGC: 当前新生代容量 (kB).
    • S0C: 当前第一个辛存者区的容量(kB).
    • S1C: 当前第二个辛存者区的容量(kB).
    • EC: 当前 Eden 区容量 (kB).
    • OGCMN: 老年代最小容量 (kB).
    • OGCMX: 老年代最大容量 (kB).
    • OGC: 当前老年代容量 (kB).
    • OC: 当前老年代容间容量 (kB).
    • MCMN: 元空间最小容量 (kB).
    • MCMX: 元空间最大容量 (kB).
    • MC: 当前元空间容量 (kB).
    • CCSMN: 压缩类空间最小容量 (kB).
    • CCSMX: 压缩类空间最大容量 (kB).
    • CCSC: 当前压缩类空间容量 (kB).
    • YGC: 年轻代 GC 事件数。
    • FGC: 老年代 GC 事件数。
  • -gccause:收集垃圾回收的摘要信息,包括上次垃圾回收和当前垃圾回收事件的原因。

    • LGCC: 上次垃圾回收原因.
    • GCC: 当前垃圾回收原因.
  • -printcompilation:JVM 编译方法统计

    • Compiled: 最近的编译方法执行的任务数.
    • Size: 最近的编译方法的字节码的字节数.
    • Type: 最近的编译方法的编译类型.
    • Method: 最近的编译方法的类名和方法名.

示例

1
2
3
4
5
6
显示 GC 的统计信息,进程ID 为3256, 采集3次,每次间隔2秒
# jstat -gc 3526 2s 3
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
512.0 512.0 64.0 0.0 86016.0 55157.3 175104.0 68536.8 18944.0 18316.3 2048.0 1899.8 802 1.548 0 0.000 1.548
512.0 512.0 64.0 0.0 86016.0 55157.3 175104.0 68536.8 18944.0 18316.3 2048.0 1899.8 802 1.548 0 0.000 1.548
512.0 512.0 64.0 0.0 86016.0 55157.3 175104.0 68536.8 18944.0 18316.3 2048.0 1899.8 802 1.548 0 0.000 1.548

jstack

jstack:打印 Java 进程的线程堆栈信息。将打印完整的类名,方法名,字节码索引和行号。

语法

1
2
3
jstack [ options ] pid
jstack [ options ] executable core
jstack [ options ] [ server-id@ ] remote-hostname-or-IP
  • pid:Java 进程 ID 号。可使用命令jps查看 Java 进程 ID列表。
  • executable:生成 Java 可执行的核心 dump 文件。
  • core:打印的栈跟踪核心文件。
  • remote-hostname-or-IP:远程调试服务器的主机名或 IP 地址。参见jsadebugd
  • server-id:当多个调试服务器在同一远程主机上运行时使用的可选唯一ID。

参数

参数说明

  • -F:当使用 jstack [-l] pid 命令无响应时,使用此参数强制堆栈转存为 dump。
  • -l:长列表,打印有关锁的附加信息。例如属于 java.util.concurrent ownable synchronizers 的列表。
  • -m:打印 Java 和本地 C/C++框架的混合模式堆栈跟踪。无法用于远程调试。

jinfo

jinfo:打印 Java 进程的配置信息。配置信息包括 Java 系统属性和 JVM 命令行标志。如果运行的是 64位 JVM,需要使用 -J-d64 参数项。例如:jinfo -J-d64 -sysprops pid

语法

1
2
3
jinfo [ option ] pid
jinfo [ option ] executable core
jinfo [ option ] [ servier-id ] remote-hostname-or-IP
  • pid:Java 进程 ID 号。可使用命令jps查看 Java 进程 ID列表。
  • executable:生成 Java 可执行的核心 dump 文件。
  • core:打印的配置信息核心文件。
  • remote-hostname-or-IP:远程调试服务器的主机名或 IP 地址。参见jsadebugd
  • server-id:当多个调试服务器在同一远程主机上运行时使用的可选唯一ID。

参数

参数说明

  • 无参数:打印命令行标志 和 系统属性 name / value 对。
  • -flag name:打印命令名的值。
  • -flag name=value:设置命令的值。
  • -flag [ + | -] name:开启或关闭 Boolean 命令。
  • -flags:打印 JVM 所有标志。
  • -sysprops:打印系统属性 name / value 对。

-falg name=value:不是所有参数可以修改的,只有部分,下面是列出可修改的参数列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# java -XX:+PrintFlagsFinal -version |grep manageable
intx CMSAbortablePrecleanWaitMillis = 100 {manageable}
intx CMSTriggerInterval = -1 {manageable}
intx CMSWaitDuration = 2000 {manageable}
bool HeapDumpAfterFullGC = false {manageable}
bool HeapDumpBeforeFullGC = false {manageable}
bool HeapDumpOnOutOfMemoryError = false {manageable}
ccstr HeapDumpPath = {manageable}
uintx MaxHeapFreeRatio = 100 {manageable}
uintx MinHeapFreeRatio = 0 {manageable}
bool PrintClassHistogram = false {manageable}
bool PrintClassHistogramAfterFullGC = false {manageable}
bool PrintClassHistogramBeforeFullGC = false {manageable}
bool PrintConcurrentLocks = false {manageable}
bool PrintGC = false {manageable}
bool PrintGCDateStamps = false {manageable}
bool PrintGCDetails = false {manageable}
bool PrintGCID = false {manageable}
bool PrintGCTimeStamps = false {manageable}
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)

java -XX:+PrintFlagsFinal -version 输出全部的参数。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
显示 Java 进程ID为 31469 的配置信息
# jinfo 31469
# jinfo -J-d64 -sysprops 31469

显示 JVM 所有标志
# jinfo -flags 31469
Attaching to process ID 31469, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.202-b08
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=67108864 -XX:+ManagementServer -XX:MaxHeapSize=1073741824 -XX:MaxNewSize=357564416 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=22020096 -XX:OldSize=45088768 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
Command line: -Xms64M -Xmx1G -Djava.util.logging.config.file=logging.properties -Djava.security.auth.login.config=/usr/local/activemq-5.15.9//conf/login.config -Dcom.sun.management.jmxremote -Djava.awt.headless=true -Djava.io.tmpdir=/usr/local/activemq-5.15.9//tmp -Dactivemq.classpath=/usr/local/activemq-5.15.9//conf:/usr/local/activemq-5.15.9//../lib/: -Dactivemq.home=/usr/local/activemq-5.15.9/ -Dactivemq.base=/usr/local/activemq-5.15.9/ -Dactivemq.conf=/usr/local/activemq-5.15.9//conf -Dactivemq.data=/usr/local/activemq-5.15.9//data

显示 JVM 标志名为 InitialHeapSize 的值
# jinfo -flag InitialHeapSize 31469

jmap

jmap:打印进程的共享对象内存映射或堆内存详细信息。如果运行的是 64 位 JVM,可使用 -J-d64选项。例如,jmap -J-d64 -heap pid

语法

1
2
3
jmap [ options ] pid
jmap [ options ] executable core
jmap [ options ] [ pid ] server-id@ ] remote-hostname-or-IP
  • pid:Java 进程 ID 号。可使用命令jps查看 Java 进程 ID列表。
  • executable:生成 Java 可执行的核心 dump 文件。
  • core:打印的配置信息核心文件。
  • remote-hostname-or-IP:远程调试服务器的主机名或 IP 地址。参见jsadebugd
  • server-id:当多个调试服务器在同一远程主机上运行时使用的可选唯一ID。

参数

  • 无参数:打印 JVM 加载的所有共享对象映射。包括直始地址,映射的大小,共享对象文件的全路径文件。类似于 Linux 的 pmap 命令。

  • -dump:[live,] format=b, file=filename

    将堆信息使用 hprof二进制格式转存为 Dump 文件。可以使用 jhat 浏览生成的 Dump 文件。

  • -finalizerinfo:打印关于正在等待完成的对象的信息。

  • -heap:打印 GC 使用的,头配置和堆的使用情况的堆摘要信息。

  • **-histo[:live]**::打印堆中对像统计数量。包括每个类的对象数量,内存大小(字节单位)及全限定类名。内部类名有星号(*)前缀。若指定了 live 子项,则仅统计活动对象。

  • -clstats:打印 Java 堆类加载的统计信息。包括每个类加载器的名称,活动程度,地址,父类加载器及已加载的类的数量和大小。

  • -F:当 jmap pid 不响应时,此参数与 jmap -dumpjmap -histo 一起使用。此模式不支持live子项。

  • -Jflag:将标志传递给运行jmap命令的 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
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
# jmap 31469
Attaching to process ID 31469, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.202-b08
0x0000000000400000 8K /usr/java/jdk1.8.0_202/bin/java
0x00007f804fdf0000 66K /usr/lib64/libbz2.so.1.0.6
0x00007f8068161000 153K /usr/lib64/liblzma.so.5.2.2
0x00007f8068387000 88K /usr/lib64/libz.so.1.2.7
.........

# jmap -heap 31469
Attaching to process ID 31469, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.202-b08

using thread-local object allocation.
Parallel GC with 2 thread(s)

Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 1073741824 (1024.0MB)
NewSize = 22020096 (21.0MB)
MaxNewSize = 357564416 (341.0MB)
OldSize = 45088768 (43.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 = 20971520 (20.0MB)
used = 3000688 (2.8616790771484375MB)
free = 17970832 (17.138320922851562MB)
14.308395385742188% used
From Space:
capacity = 1572864 (1.5MB)
used = 0 (0.0MB)
free = 1572864 (1.5MB)
0.0% used
To Space:
capacity = 3670016 (3.5MB)
used = 0 (0.0MB)
free = 3670016 (3.5MB)
0.0% used
PS Old Generation
capacity = 52428800 (50.0MB)
used = 20475112 (19.526588439941406MB)
free = 31953688 (30.473411560058594MB)
39.05317687988281% used

15961 interned Strings occupying 1402992 bytes.

# jmap -dump:live,format=b,file=dump 31469
Dumping heap to /root/dump ...
Heap dump file created

输出头 10 行数据
# jmap -histo:live 31469 | head -10
num #instances #bytes class name
----------------------------------------------
1: 1307 4650864 [B
2: 29747 2994768 [C
3: 29305 703320 java.lang.String
4: 5684 634400 java.lang.Class
5: 12000 384000 java.util.concurrent.ConcurrentHashMap$Node
6: 5919 379240 [Ljava.lang.Object;
7: 6870 219840 java.lang.ref.WeakReference

jhat

jhat:创建一个用于分析 Java Heap Dump 文件的 HTTP 服务器,可以使用 Web 浏览器浏览堆的 Dump 文件。

生成堆的 Dump 文件方式有:

  • 使用 jmap -dump 获取运行时的堆的 Dump。
  • 使用 jconsole 选项,通过 HotSpotDiagnosticMXBean 获取运行时的堆 Dump。
  • 给 JVM 指定 -XX:+HeapDumpOnOutOfMemoryError,当抛出 OutOfMemoryError 错误时会生成堆的 Dump。
  • 使用 hprof 命令。一个 Heap/CPU 分析工具,参考 HPROF

语法

1
jhat [ options ] heap-dump-file
  • heap-dump-file:Java 二进制 Dump 文件。

参数

  • -stack false|true:关闭或开启跟踪对象分配调用栈。如果 Heap Dump 中没有分配站点信息,则必须将此标志设置为 false,默认为 true
  • -refs false|true:关闭或开启对象引用跟踪。默认为 true
  • -port port-number:设置 jhat HTTP 服务的端口,默认为 7000
  • -exclude exclude-file:指定一个文件,该文件列出应从可访问对象查询中排除的数据成员。
  • -baseline exclude-file:指定基线 Heap Dump。两个 Heap Dump 中具有相同对象ID的对象都被标记为非新对象,其他对象被标记为新对象,这对于比较两个不同的 Heap Dump 非常有用。
  • -debug int:设置 Debug 级别。0 表示无 Debug 输出。
  • -Jflag:将标志传递给运行jhat令的 Java虚拟机。例如,-J-Xmx512m使用最大堆大小为 512 MB。

示例

1
2
3
4
5
6
7
8
9
10
11
12
# jhat -port 8000 dump
Reading from dump...
Dump file created Wed Feb 05 17:50:14 CST 2020
Snapshot read, resolving...
Resolving 308168 objects...
Chasing references, expect 61 dots.............................................................
Eliminating duplicate references.............................................................
Snapshot resolved.
Started HTTP server on port 8000
Server is ready.

就可以通过浏览器访问服务地址,可以对 Heap Dump 进行分析

jstatd

jstatd:是一个 RMI 服务应用,用于监视 JVM 和 开启 JVM 的远程监控服务,允许远程监视工具连接到本地的 JVM 上。

语法

1
jstatd [ options ]

参数

参数说明

  • -nr:当找不到现有的 RMI 注册表时,不要尝试在 jstatd 进程中创建内部 RMI 注册表。
  • -p port:设置 RMI 服务端口号。
  • -n rminame:远程 RMI 对象绑定到 RMI 注册表中的名称。
  • -Joption:将 option 传递给 JVM。例如,-J-Xms48m 将启动内存设置为 48M。

远程连接

  1. 安全策略

    使该命令可能会报 无访问权限 的错误,需要配置访问策略。错误如下:

    1
    2
    3
    4
    5
    6
    7
    8
    Could not create remote object
    access denied ("java.util.PropertyPermission" "java.rmi.server.ignoreSubClasses
    java.security.AccessControlException: access denied ("java.util.PropertyPermissmi.server.ignoreSubClasses" "write")
    at java.security.AccessControlContext.checkPermission(AccessControlCont)
    at java.security.AccessController.checkPermission(AccessController.java
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
    at java.lang.System.setProperty(System.java:792)
    at sun.tools.jstatd.Jstatd.main(Jstatd.java:139)

    在 Java 安装目录的 bin 文件夹下创建访问策略文件,例如:jstatd.all.policy,内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    grant codebase "file:${java.home}/../lib/tools.jar" {
    permission java.security.AllPermission;
    };

    #示例:
    grant codebase "file:/usr/java/jdk1.8.0_202/lib/tools.jar" {
    permission java.security.AllPermission;
    };
  2. 启动服务

    下面的 host_ip 就是服务器的 IP 地址,如果是阿里云服务器,就是外网的 IP 地址。

    1
    2
    3
    4
    jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=host_ip

    #后台运行
    nohup ./jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=host_ip &
  3. 本地验证是否启动成功

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # jps -l
    3526 org.apache.catalina.startup.Bootstrap
    2827 sun.tools.jstatd.Jstatd
    31469 /usr/local/activemq-5.15.9//bin/activemq.jar
    2926 sun.tools.jps.Jps

    # netstat -anp|grep jstatd
    tcp6 0 0 :::1099 :::* LISTEN 2827/jstatd
    tcp6 0 0 :::14616 :::* LISTEN 2827/jstatd
    tcp6 0 0 172.31.97.209:1099 120.229.16.135:9763 ESTABLISHED 2827/jstatd
    unix 2 [ ] STREAM CONNECTED 6495795 2827/jstatd

    注意:通过 netstat 命令可以看到 jstatd 服务监听了 2 个端口,1099是固定端口,远程客户端会先向这个端口请求请求一个RMI远程对象,再与 14616 建立连接,进行点对点通信。14616是个随机端口,每次重次启动 jstatd 服务可能不一样。

  4. 开放端口

    如果服务器本地防火墙配置了端口安全策略,就需要开放这两个端口。

    如果是阿里云服务器,需要配置安全策略,开启放两个端口,外部客户端才可以访问。

  5. 外部验证是否可访问

    外部命令窗口可以用 jps host_ip 来验证是否可访问。

  6. Java VisualVM 连接

    打开 Java 安装目录下的 bin 文件夹中的 jvisualvm.exe 工具,添加远程主机,就会自动列出该主机下运行的所有 Java 进程。

jcmd

jcmd 是个多功能功工具,可以用于导出堆,查看 Java 进程,导出线程,执行 GC 等。必须在运行 JVM 的同一机器上使用。

语法

1
2
3
4
jcmd [-l|-h|-help]
jcmd pid|main-class PerfCounter.print
jcmd pid|main-class -f filename
jcmd pid|main-class command[ arguments]

如果运行 jcmd 不带参数,或带 -l 参数,将打印用于启动进程的主类 和 正中运行的 Java 进程 ID 列表。

jcmd 拥有 jmap 的大部分功能,且 Oracle 官网也推荐使用此命令代替 jmap

jcmd 默认参数做 heap dump 之前会做一次 Full GC,内存基本就剩下活着的对象,Full GC 也会对元数据区进行回收,就不易于分析分析元数据内存溢出的情况,可以指定参数不进行 Full GC 的 heap dump。如下:

1
jcmd [pid] GC.heap_dump -all

参数

  • -l:打印正在运行 Java 的进程号、启动类和命令行参数。

示例

  1. jcmd -l 命令

    打印 Java 进程号,启动类,运行参数

    1
    2
    3
    # jcmd -l
    12088 org.apache.catalina.startup.Bootstrap start
    32697 sun.tools.jcmd.JCmd -l
  2. 查看进程号虚拟机所支持的 jcmd 命令操作

    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
    # jcmd 12088 help
    12088:
    The following commands are available:
    The following commands are available:
    JFR.stop
    JFR.start
    JFR.dump
    JFR.check
    VM.native_memory
    VM.check_commercial_features
    VM.unlock_commercial_features
    ManagementAgent.stop
    ManagementAgent.start_local
    ManagementAgent.start
    VM.classloader_stats
    GC.rotate_log
    Thread.print
    GC.class_stats
    GC.class_histogram
    GC.heap_dump
    GC.finalizer_info
    GC.heap_info
    GC.run_finalization
    GC.run
    VM.uptime
    VM.dynlibs
    VM.flags
    VM.system_properties
    VM.command_line
    VM.version
    help
  3. 查看 JVM 启动时间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // 根据进程号查看
    # jcmd 12088 VM.uptime
    12088:
    2763.245 s

    // 根据启动类来查看
    # jcmd com.springboot.rabbitmq.Application VM.uptime
    12088:
    2834.344 s

    // 查看GC堆信息
    # jcmd 12088 GC.heap_info
    29452:
    PSYoungGen total 228864K, used 83610K [0x00000000eab00000, 0x00000000fa200000, 0x0000000100000000)
    eden space 214016K, 39% used [0x00000000eab00000,0x00000000efca6bd0,0x00000000f7c00000)
    from space 14848K, 0% used [0x00000000f7c00000,0x00000000f7c00000,0x00000000f8a80000)
    to space 14336K, 0% used [0x00000000f9400000,0x00000000f9400000,0x00000000fa200000)
    ParOldGen total 196096K, used 12482K [0x00000000c0000000, 0x00000000cbf80000, 0x00000000eab00000)
    object space 196096K, 6% used [0x00000000c0000000,0x00000000c0c309e8,0x00000000cbf80000)
    Metaspace used 30967K, capacity 31362K, committed 31744K, reserved 1077248K
    class space used 3677K, capacity 3793K, committed 3840K, reserved 1048576K

  4. 打印线程栈

    1
    # jcmd 12088 Thread.print
  5. 查看系统中类的统计信息

    1
    # jcmd 12088 GC.class_histogram
  6. 获取系统属性

    1
    # jcmd 12088 VM.system_properties
  7. 获取 PerData 数据

    1
    # jcmd 12088 PerfCounter.print
  8. 获取 JVM 启动参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # jcmd 12088 VM.flags
    12088:
    -XX:-BytecodeVerificationLocal
    -XX:-BytecodeVerificationRemote
    -XX:CICompilerCount=4
    -XX:InitialHeapSize=268435456
    -XX:+ManagementServer
    -XX:MaxHeapSize=4263510016
    -XX:MaxNewSize=1420820480
    -XX:MinHeapDeltaBytes=524288
    -XX:NewSize=89128960
    -XX:OldSize=179306496
    -XX:TieredStopAtLevel=1
    -XX:+UseCompressedClassPointers
    -XX:+UseCompressedOops
    -XX:+UseFastUnorderedTimeStamps
    -XX:-UseLargePagesIndividualAllocation
    -XX:+UseParallelGC

hprof

hprof 不是独立的监控工具,只是一个 Java agent 工具。可用于监控 Java 应用在运行时的 CPU 信息和堆信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# java -agentlib:hprof=help
HPROF: Heap and CPU Profiling Agent (JVMTI Demonstration Code)
hprof usage: java -agentlib:hprof=[help]|[<option>=<value>, ...]

Option Name and Value Description Default
--------------------- ----------- -------
heap=dump|sites|all heap profiling all
cpu=samples|times|old CPU usage off
monitor=y|n monitor contention n
format=a|b text(txt) or binary output a
file=<file> write data to file java.hprof[{.txt}]
net=<host>:<port> send data over a socket off
depth=<size> stack trace depth 4
interval=<ms> sample interval in ms 10
cutoff=<value> output cutoff point 0.0001
lineno=y|n line number in traces? y
thread=y|n thread in traces? n
doe=y|n dump on exit? y
msa=y|n Solaris micro state accounting n
force=y|n force output to <file> y
verbose=y|n print messages about dumps y

参数使用示例

  1. times 会在 Java 方法的调用前后记录方法的执行时间,进而计算方法的运行时间。

    1
    # java -agentlib:hprof=cpu=times,interval=10 /HProfTest.java
  2. 将应用的堆快照保存到指定文件 c:\core.hprof

    1
    # java -agentlib:hprof=heap=dump,format=b,file=c:\core.hprof d:\HProfTest.java
  3. 查看每个跟踪点上的类所占内存的百分比

    1
    # java -agentlib:hprof=heap=sites

    结果:300010 跟踪点使用内存比 3.71%,20672 个字节

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    TRACE 300010:
    java.util.Arrays.copyOf(<Unknown Source>:Unknown line)
    java.lang.AbstractStringBuilder.ensureCapacityInternal(<Unknown Source>:Unknown line)
    java.lang.AbstractStringBuilder.append(<Unknown Source>:Unknown line)
    java.lang.StringBuilder.append(<Unknown Source>:Unknown line)

    rank self accum bytes objs bytes objs trace name
    1 3.71% 3.71% 20672 139 20672 139 300010 char[]
    2 3.32% 7.03% 18520 288 18520 288 300311 char[]
    3 2.94% 9.97% 16416 2 16416 2 300304 byte[]

其它参考

JVM虚拟机(1):JVM 监控调优-自带命令

http://blog.gxitsky.com/2020/02/03/JVM-01-jdk-command-monitor/

作者

光星

发布于

2020-02-03

更新于

2022-06-17

许可协议

评论