在Java层面(window和Linux系统)下的常用性能监控与调优工具的命令及操作

18 篇文章 0 订阅
5 篇文章 0 订阅

前言

总结,学习,发现问题,再总结,避免以后,在需要的时候抓狂!

jvm中的gc的参数解释

年轻代survivor(幸存区) S0C年轻代中第一个survivor(幸存区)的容量(kb)
S1C年轻代中第二个survivor(幸存区)的容量(kb)
已使用S0U年轻代中第一个survivor(幸存区)目前已使用空间(kb)
S1U年轻代中第二个survivor(幸存区)目前已使用空间(kb)
年轻代中Eden(伊甸园) EC年轻代中Eden(伊甸园)的容量(kb)
EU年轻代中Eden(伊甸园)目前已使用空间(kb)
Old代 OCOld代的容量(kb)
OUOld代目前已使用空间(kb)
Perm(持久代) PCPerm(持久代)的容量(kb)
PUPerm(持久代)目前已使用空间(kb)
从应用程序启动到
采样时年轻代中gc
 YGC从应用程序启动到采样时年轻代中gc次数
YGCT从应用程序启动到采样时年轻代中gc所用时间(s)
从应用程序启动到
采样时old代
old代(全gc)FGC从应用程序启动到采样时old代(全gc)gc次数
FGCT从应用程序启动到采样时old代(全gc)gc所用时间(s)
 GCT从应用程序启动到采样时gc用的总时间(s)
NGCMN年轻代(young)中初始化(最小)的大小(kb)
年轻代(young) NGCMX年轻代(young)的最大容量(kb)
NGC年轻代(young)中当前的容量(kb)
old代最小最大OGCMNold代中初始化(最小)的大小(kb)
OGCMXold代的最大容量(kb)
OGCold代当前新生成的容量(kb)
perm代容量 PGCMNperm代中初始化(最小)的大小(kb)
PGCMXperm代的最大容量(kb)
PGCperm代当前新生成的容量(kb)
年轻代已使用的
占当前容量百分比
survivor(幸存区)S0年轻代中第一个survivor(幸存区)已使用的占当前容量百分比
S1年轻代中第二个survivor(幸存区)已使用的占当前容量百分比
 E年轻代中Eden(伊甸园)已使用的占当前容量百分比
  Oold代已使用的占当前容量百分比
Pperm代已使用的占当前容量百分比
年轻代最大容量(kb)survivor(幸存区)S0CMX年轻代中第一个survivor(幸存区)的最大容量(kb)
S1CMX年轻代中第二个survivor(幸存区)的最大容量(kb)
  ECMX年轻代中Eden(伊甸园)的最大容量(kb)
  DSS当前需要survivor(幸存区)的容量(kb)(Eden区已满)
次数限制 TT持有次数限制
MTT最大持有次数限制

Java层面的工具命令

所谓的Java层面的命令工具,就是Java虚拟机的监控及诊断工具。今将使用刚刚发布的 Java 11 版本的工具,进行演示 JDK 中用于监控及诊断工具。

jps

你可能用过ps命令,打印所有正在运行的进程的相关信息。JDK 中的jps命令(帮助文档)沿用了同样的概念:它将打印所有正在运行的 Java 进程的相关信息。

在默认情况下,jps的输出信息,包括 Java 进程的进程 ID 及主类名。我们还可以通过追加参数,来打印额外的信息。例如:

  • -l将打印模块名以及包名
  • -v将打印传递给 Java 虚拟机的参数(如-XX:+UnlockExperimentalVMOptions -XX:+UseZGC);
  • -m将打印传递给主类的参数。

具体的示例如下所示:

$ jps -mlv
18331 org.example.Foo Hello World
18332 jdk.jcmd/sun.tools.jps.Jps -mlv -Dapplication.home=/Library/Java/JavaVirtualMachines/jdk-11.jdk/Contents/Home -Xms8m -Djdk.module.main=jdk.jcmd

需要注意的是,如果某 Java 进程关闭了默认开启的UsePerfData参数(即使用参数-XX:-UsePerfData),那么jps命令(以及下面介绍的jstat)将无法探知该 Java 进程。

当获得 Java 进程的进程 ID 之后,我们便可以调用,接下来介绍的各项监控,及诊断工具了

jstat

很多人还不会 jstat 命令!它是JDK自带的一个轻量级小工具。全称“Java Virtual Machine statistics monitoring tool”。
 

Jstat位于java的bin目录下,主要利用JVM内建的指令对Java应用程序的资源性能进行实时的命令行的监控包括了对Heap size和垃圾回收状况的监控。

Jstat可以用来监视VM内存内的各种堆非堆的大小其内存使用量

默认情况下jstat只会打印一次性能数据。我们可以将它配置为每隔一段时间打印一次直至目标 Java 进程终止或者达到我们所配置的最大打印次数。具体示例如下所示:
# Usage: jstat -outputOptions [-t] [-hlines] VMID [interval [count]]

jstat -gc pid

jstat -gc pid 可以显示gc的信息查看gc的次数及时间。其中最后五项,分别是young gc的次数young gc的时间full gc的次数full gc的时间gc的总时间

[root@fleapx ~]# jstat -gc 5801
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT
15360.0 12288.0  0.0   11964.6 298496.0 16530.5   67072.0    35793.4   83968.0 58633.2     21    0.366   1      0.480    0.846
$ jstat -gc 22126 1s 4
S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
17472.0 17472.0  0.0    0.0   139904.0 47146.4   349568.0   21321.0   30020.0 28001.8 4864.0 4673.4     22    0.080   3      0.270   0      0.000    0.350
17472.0 17472.0 420.6   0.0   139904.0 11178.4   349568.0   21321.0   30020.0 28090.1 4864.0 4674.2     28    0.084   3      0.270   0      0.000    0.354
17472.0 17472.0  0.0   403.9  139904.0 139538.4  349568.0   21323.4   30020.0 28137.2 4864.0 4674.2     34    0.088   4      0.359   0      0.000    0.446
17472.0 17472.0  0.0    0.0   139904.0   0.0     349568.0   21326.1   30020.0 28093.6 4864.0 4673.4     38    0.091   5      0.445   0      0.000    0.536

当监控本地环境的 Java 进程时VMID 可以简单理解为 PID。
如果需要监控远程环境的 Java 进程,你可以参考 jstat 的帮助文档。
在上面这个示例中,22126 进程是一个使用了 CMS 垃圾回收器的 Java 进程。我们利用jstat的-gc子命令,来打印该进程垃圾回收相关的数据。命令最后的1s 4,表示每隔 1 秒打印一次,共打印 4 次。

-gc子命令的输出中,前四列分别为两个 Survivor 区容量(Capacity)和已使用量(Utility)。我们可以看到,这两个 Survivor 区的容量相等,而且始终有一个 Survivor 区的内存使用量为 0

当使用默认的 G1 GC 时,输出结果则有另一些特征:

[root@fleapx ~]# jstat -gc 22208 1s
S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
0.0   16384.0  0.0   16384.0 210944.0 192512.0  133120.0    5332.5   28848.0 26886.4 4864.0 4620.5     19    0.067   1      0.016   2      0.002    0.084
0.0   16384.0  0.0   16384.0 210944.0 83968.0   133120.0    5749.9   29104.0 27132.8 4864.0 4621.0     21    0.078   1      0.016   2      0.002    0.095
0.0    0.0    0.0    0.0   71680.0  18432.0   45056.0    20285.1   29872.0 27952.4 4864.0 4671.6     23    0.089   2      0.063   2      0.002    0.153
0.0   2048.0  0.0   2048.0 69632.0  28672.0   45056.0    18608.1   30128.0 28030.4 4864.0 4672.4     32    0.093   2      0.063   2      0.002    0.158
...

在上面这个示例中,jstat每隔 1s 便会打印垃圾回收的信息,并且不断重复下去。

你可能已经留意到,S0C和S0U始终为 0,而且另一个 Survivor 区的容量(S1C)可能会下降至 0。

这是因为,当使用 G1 GC 时,Java 虚拟机不再设置 Eden 区Survivor 区老年代区的内存边界,而是将堆划分为若干个等长内存区域。

每个内存区域都可以作为 Eden 区、Survivor 区以及老年代区中的任一种,并且可以在不同区域类型之间来回切换

换句话说,逻辑上,我们只有一个 Survivor 区。当需要迁移 Survivor 区中的数据时(即 Copying GC),我们只需另外申请一个多个内存区域,作为新的 Survivor 区

因此,Java 虚拟机决定在使用 G1 GC 时,将所有 Survivor 内存区域的总容量,以及已使用量存放至 S1CS1U 中,而 S0CS0U 则被设置为 0。

当发生垃圾回收时,Java 虚拟机可能出现 Survivor 内存区域内的对象,全被回收晋升的现象

在这种情况下,Java 虚拟机会将这块内存区域回收并标记可分配的状态。这样做的结果是,堆中可能完全没有 Survivor 内存区域,因而相应的 S1C S1U 将会是 0。

jstat还有一个非常有用的参数-t,它将在每行数据之前,打印目标 Java 进程启动时间。例如,在下面这个示例中,第一列代表该 Java 进程已经启动了 10.7 秒。

[root@fleapx ~]# jstat -gc -t 22407
Timestamp  S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
10.7      0.0    0.0    0.0    0.0   55296.0  45056.0   34816.0  20267.8   30128.0 27975.3 4864.0 4671.6  33   0.086   3   0.111   2   0.001   0.198

我们可以比较 Java 进程的启动时间,以及总 GC 时间(GCT 列),或者两次测量的间隔时间以及总 GC 时间的增量,来得出 GC 时间占运行时间的比例

  • 如果该比例超过 20%,则说明目前堆的压力较大
  • 如果该比例超过 90%,则说明堆里几乎没有可用空间随时都可能抛出 OOM 异常

jstat,还可以用来判断是否出现内存泄漏在长时间运行的 Java 程序中,我们可以运行jstat命令,连续获取多行性能数据,并取这几行数据中 OU 列(即已占用的老年代内存)的最小值。

然后,我们每隔一段较长的时间,重复一次上述操作,来获得多组 OU 最小值。如果这些值,呈上涨趋势,则说明该 Java 程序的老年代内存已使用量不断上涨,这意味着,无法回收的对象,在不断增加,因此很有可能存在内存泄漏。

上面没有涉及的列(或者其他子命令的输出),你可以查阅帮助文档了解具体含义。

至于文档中漏掉的 CGCCGCT,它们分别代表并发 GC Stop-The-World 的次数和时间。

jstat -gcutil pid

通过 jstat -gcutil pid 命令,我们可以对gc信息进行统计。

[root@fleapx ~]# jstat -gcutil 5801
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
  0.00  97.37   5.54  53.37  69.83     21    0.366     1    0.480    0.846

其中 5801 就是 pid,也就是你的进程id,可以通过多种途径获取你的进程id,例如 jps 命令等。

jstat -gccapacity pid

jstat -gccapacity pid命令可以显示:VM内存中三代(young,old,perm)对象使用占用大小。如:

  • PGCMN显示的是最小perm的内存使用量,
  • PGCMX显示的是perm的内存最大使用量,
  • PGC是当前新生成的perm内存占用量,
  • PC是前perm内存占用量。其他的可以根据这个类推,
  • OC是old内存的占用量。
[root@fleapx ~]# jstat -gccapacity 5801
 NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC         OC      PGCMN    PGCMX     PGC       PC     YGC    FGC
 20480.0 327168.0 327168.0 15360.0 12288.0 298496.0    40448.0   653824.0    67072.0    67072.0  21504.0  83968.0  83968.0  83968.0     21     1

jstat -gcnew pid

jstat -gcnew pid 命令可以显示年轻代对象的信息。

[root@fleapx ~]# jstat -gcnew 5801
 S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT
15360.0 12288.0    0.0 11964.6  3  15 15360.0 298496.0  16563.7     21    0.366

jstat -gcnewcapacity pid

jstat -gcnewcapacity pid 命令可以显示年轻代对象的信息其占用量

[root@fleapx ~]# jstat -gcnewcapacity 5801
  NGCMN      NGCMX       NGC      S0CMX     S0C     S1CMX     S1C       ECMX        EC      YGC   FGC
20480.0   327168.0   327168.0 109056.0  15360.0 109056.0  12288.0   326144.0   298496.0    21     1

jstat -gcold pid

jstat -gcold pid 命令可以显示old代对象的信息。

[root@fleapx ~]# jstat -gcold 5801
   PC       PU        OC          OU       YGC    FGC    FGCT     GCT
 83968.0  58639.1     67072.0     35793.4     21     1    0.480    0.846

jstat -gcoldcapacity pid

jstat -gcoldcapacity pid 命令可以显示old代对象的信息其占用量

[root@fleapx ~]# jstat -gcoldcapacity 5801
   OGCMN       OGCMX        OGC         OC       YGC   FGC    FGCT     GCT
  40448.0    653824.0     67072.0     67072.0    21     1    0.480    0.846

jstat -class pid

jstat -class pid 命令可以显示加载class的数量,及所占空间等信息

[root@fleapx ~]# jstat -class 5801
Loaded  Bytes  Unloaded  Bytes     Time
10924  20744.5        0     0.0      13.11

jstat -compiler pid

jstat -compiler pid 命令可以显示VM实时编译的数量信息

[root@fleapx ~]# jstat -compiler 5801
Compiled Failed Invalid   Time   FailedType FailedMethod
 1452      2       0    54.32        1   java/net/URL openConnection

jstat -printcompilation pid

jstat -printcompilation pid 命令可以显示当前VM执行的信息。

[root@fleapx ~]# jstat -printcompilation 5801
Compiled  Size  Type               Method
1453       13    1    java/util/concurrent/atomic/AtomicBoolean get

jmap

在这种情况下,我们便可以请jmap命令(帮助文档)出马,分析 Java 虚拟机堆中的对象。

jmap同样包括多条子命令。

-clstats,该子命令将打印被加载类的信息

-finalizerinfo,该子命令将打印所有待 finalize 的对象。

-histo,该子命令将统计各个类的实例数目,以及占用内存,并按照内存使用量,从多至少的顺序排列。此外,-histo:live只统计堆中的存活对象。

-dump,该子命令将导出 Java 虚拟机堆的快照。同样,-dump:live只保存堆中的存活对象

我们通常会利用jmap -dump:live,format=b,file=filename.bin命令,将堆中所有存活对象导出至一个文件之中

这里format=b,将使jmap导出与hprof在 Java 9 中已被移除)、-XX:+HeapDumpAfterFullGC、-XX:+HeapDumpOnOutOfMemoryError格式一致的文件。这种格式的文件,可以被其他 GUI 工具查看,具体我会在下一篇中进行演示。

jmap -histo pid命令的输出:

[root@fleapx ~]# jmap -histo 22574
 num     #instances         #bytes  class name (module)
-------------------------------------------------------
   1:        500004       20000160  org.python.core.PyComplex
   2:        570866       18267712  org.python.core.PyFloat
   3:        360295       18027024  [B (java.base@11)
   4:        339394       11429680  [Lorg.python.core.PyObject;
   5:        308637       11194264  [Ljava.lang.Object; (java.base@11)
   6:        301378        9291664  [I (java.base@11)
   7:        225103        9004120  java.math.BigInteger (java.base@11)
   8:        507362        8117792  org.python.core.PySequence$1
   9:        285009        6840216  org.python.core.PyLong
  10:        282908        6789792  java.lang.String (java.base@11)
  ...
2281:             1             16  traceback$py
2282:             1             16  unicodedata$py
Total       5151277      167944400

由于jmap访问堆中的所有对象,为了保证,在此过程中,不被应用线程干扰,jmap需要借助安全点机制,让所有线程停留不改变堆中数据的状态。

也就是说,由jmap导出堆快照,必定是安全点位置的。这可能导致基于该堆快照分析结果存在偏差。举个例子,假设在编译生成的机器码中某些对象的生命周期在两个安全点之间,那么:live选项将无法探知到这些对象。

另外,如果某个线程,长时间无法跑到安全点,jmap将一直等下去与上面的jstat则不同。这是因为,垃圾回收器,会主动将jstat所需要的摘要数据,保存至固定位置之中,而jstat只需直接读取即可。

关于这种长时间等待的情况,你可以通过下面这段程序来复现

// 暂停时间较长,约为二三十秒,可酌情调整。
// CTRL+C 的 SIGINT 信号无法停止,需要 SIGKILL。
static double sum = 0;
 
public static void main(String[] args) {
  for (int i = 0; i < 0x77777777; i++) { // counted loop
    sum += Math.log(i); // Math.log is an intrinsic
  }
}

jmap(以及接下来的jinfojstackjcmd依赖于 Java 虚拟机的Attach API,因此只能监控本地 Java 进程

一旦开启 Java 虚拟机参数DisableAttachMechanism(即使用参数-XX:+DisableAttachMechanism),基于 Attach API 的命令,将无法执行。反过来说,如果你不想被其他进程监控那么你需要开启该参数。

jinfo

jinfo命令(帮助文档)可用来查看目标 Java 进程的参数,如传递给 Java 虚拟机的-X(即输出中的 jvm_args)、-XX参数(即输出中的 VM Flags),以及可在 Java 层面通过System.getProperty获取的-D参数(即输出中的 System Properties)。

具体的示例如下所示:

[root@fleapx ~]# jinfo 31185
Java System Properties:
 
gopherProxySet=false
awt.toolkit=sun.lwawt.macosx.LWCToolkit
java.specification.version=11
sun.cpu.isalist=
sun.jnu.encoding=UTF-8
...
 
VM Flags:
-XX:CICompilerCount=4 -XX:ConcGCThreads=3 -XX:G1ConcRefinementThreads=10 -XX:G1HeapRegionSize=2097152 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=536870912 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=8589934592 -XX:MaxNewSize=5152702464 -XX:MinHeapDeltaBytes=2097152 -XX:NonNMethodCodeHeapSize=5835340 -XX:NonProfiledCodeHeapSize=122911450 -XX:ProfiledCodeHeapSize=122911450 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC
 
VM Arguments:
jvm_args: -Xlog:gc -Xmx1024m
java_command: org.example.Foo
java_class_path (initial): .
Launcher Type: SUN_STANDARD

jinfo还可以用来修改目标 Java 进程的“manageable”虚拟机参数。

举个例子,我们可以使用jinfo -flag +HeapDumpAfterFullGC 命令,开启所指定的 Java 进程的HeapDumpAfterFullGC参数。

你可以通过下述命令,查看其他 “manageable” 虚拟机参数:

[root@fleapx ~]# java -XX:+PrintFlagsFinal -version | grep manageable   
     intx CMSAbortablePrecleanWaitMillis           = 100                                    {manageable} {default}
     intx CMSTriggerInterval                       = -1                                     {manageable} {default}
     intx CMSWaitDuration                          = 2000                                   {manageable} {default}
     bool HeapDumpAfterFullGC                      = false                                  {manageable} {default}
     bool HeapDumpBeforeFullGC                     = false                                  {manageable} {default}
     bool HeapDumpOnOutOfMemoryError               = false                                  {manageable} {default}
    ccstr HeapDumpPath                             =                                        {manageable} {default}
    uintx MaxHeapFreeRatio                         = 70                                     {manageable} {default}
    uintx MinHeapFreeRatio                         = 40                                     {manageable} {default}
     bool PrintClassHistogram                      = false                                  {manageable} {default}
     bool PrintConcurrentLocks                     = false                                  {manageable} {default}
java version "11" 2018-09-25
Java(TM) SE Runtime Environment 18.9 (build 11+28)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11+28, mixed mode)

jstack

jstack命令(帮助文档)可以用来打印目标 Java 进程各个线程的栈轨迹,以及这些线程所持有的锁

jstack的其中一个应用场景便是死锁检测。这里我用jstack获取,一个已经死锁了Java 程序的栈信息。具体输出如下所示:

[root@fleapx ~]# jstack 31634
...
 
"Thread-0" #12 prio=5 os_prio=31 cpu=1.32ms elapsed=34.24s tid=0x00007fb08601c800 nid=0x5d03 waiting for monitor entry  [0x000070000bc7e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
 at DeadLock.foo(DeadLock.java:18)
 - waiting to lock <0x000000061ff904c0> (a java.lang.Object)
 - locked <0x000000061ff904b0> (a java.lang.Object)
 at DeadLock$$Lambda$1/0x0000000800060840.run(Unknown Source)
 at java.lang.Thread.run(java.base@11/Thread.java:834)
 
"Thread-1" #13 prio=5 os_prio=31 cpu=1.43ms elapsed=34.24s tid=0x00007fb08601f800 nid=0x5f03 waiting for monitor entry  [0x000070000bd81000]
   java.lang.Thread.State: BLOCKED (on object monitor)
 at DeadLock.bar(DeadLock.java:33)
 - waiting to lock <0x000000061ff904b0> (a java.lang.Object)
 - locked <0x000000061ff904c0> (a java.lang.Object)
 at DeadLock$$Lambda$2/0x0000000800063040.run(Unknown Source)
 at java.lang.Thread.run(java.base@11/Thread.java:834)
 
...
 
JNI global refs: 6, weak refs: 0
 
 
Found one Java-level deadlock:
=============================
"Thread-0":
  waiting to lock monitor 0x00007fb083015900 (object 0x000000061ff904c0, a java.lang.Object),
  which is held by "Thread-1"
"Thread-1":
  waiting to lock monitor 0x00007fb083015800 (object 0x000000061ff904b0, a java.lang.Object),
  which is held by "Thread-0"
 
Java stack information for the threads listed above:
===================================================
"Thread-0":
 at DeadLock.foo(DeadLock.java:18)
 - waiting to lock <0x000000061ff904c0> (a java.lang.Object)
 - locked <0x000000061ff904b0> (a java.lang.Object)
 at DeadLock$$Lambda$1/0x0000000800060840.run(Unknown Source)
 at java.lang.Thread.run(java.base@11/Thread.java:834)
"Thread-1":
 at DeadLock.bar(DeadLock.java:33)
 - waiting to lock <0x000000061ff904b0> (a java.lang.Object)
 - locked <0x000000061ff904c0> (a java.lang.Object)
 at DeadLock$$Lambda$2/0x0000000800063040.run(Unknown Source)
 at java.lang.Thread.run(java.base@11/Thread.java:834)
 
Found 1 deadlock.

我们可以看到,jstack不仅会打印线程栈轨迹线程状态(BLOCKED)、持有的锁(locked …)以及正在请求的锁(waiting to lock …),而且还会分析出具体的死锁

jcmd

在JDK1.7以后,新增了一个命令行工具 jcmd。是一个多功能的工具,可以用它来导出堆查看Java进程导出线程信息执行GC、还可以进行采样分析(jmc 工具的飞行记录器)。

你还可以直接使用jcmd命令(帮助文档),来替代前面除了jstat之外的所有命令。

至于jstat的功能,虽然jcmd复制了jstat的部分代码,并支持通过PerfCounter.print子命令,打印所有的 Performance Counter,但是它没有保留jstat的输出格式,也没有重复打印的功能

$ jcmd -help
Usage: jcmd <pid | main class> <command ...|PerfCounter.print|-f file>
   or: jcmd -l
   or: jcmd -h

  command must be a valid jcmd command for the selected jvm.
  Use the command "help" to see which commands are available.
  If the pid is 0, commands will be sent to all Java processes.
  The main class argument will be used to match (either partially
  or fully) the class used to start Java.
  If no options are given, lists Java processes (same as -p).

  PerfCounter.print display the counters exposed by this process
  -f  read and execute commands from the file
  -l  list JVM processes on the local machine
  -h  this help

参数描述

  1. pid接收诊断命令请求进程ID。
  2. main class :接收诊断命令,请求的进程的main类。匹配进程时,main类名称中,包含指定子字符串的任何进程均是匹配的。如果多个正在运行的Java进程,共享同一个main类,诊断命令请求,将会发送到所有的这些进程中
  3. command:接收诊断命令,请求的进程的main类。匹配进程时,main类名称,包含指定子字符串的任何进程,均是匹配的。如果多个正在运行的Java进程,共享同一个main类,诊断命令请求,将会发送到所有的这些进程中

注意: 如果任何参数含有空格,你必须使用英文的单引号双引号将其包围起来。

此外,你必须使用转义字符来,转移参数中的单引号双引号,以阻止操作系统shell处理这些引用标记。当然,你也可以在参数两侧,加上单引号,然后在参数内,使用双引号(或者在参数两侧加上双引号在参数中使用单引号)。

  1. Perfcounter.print:打印目标Java进程上,可用的性能计数器性能计数器的列表,可能会,随着Java进程的不同,而产生变化。
  2. -f file:从文件file中读取命令,然后在目标Java进程上调用这些命令。在file中,每个命令必须写在单独的一行。以"#"开头的行,会被忽略。当所有行的命令,被调用完毕后,或者读取到含有stop关键字的命令,将会终止对file的处理
  3. -l:查看所有的进程列表信息。
  4. -h:查看帮助信息。(同 -help)
  • 查看进程
jcmd
jcmd -l
jps -m
#以上三个命令的效果是一样的

描述:查看 当前机器上所有的 jvm 进程信息

查看性能统计

命令:jcmd  <pid>  PerfCounter.print
描述:查看指定进程性能统计信息

$ jcmd 22912 PerfCounter.print
22912:
java.ci.totalTime=45473763
java.cls.loadedClasses=3350
java.cls.sharedLoadedClasses=0
java.cls.sharedUnloadedClasses=0
java.cls.unloadedClasses=0
java.property.java.class.path="D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/jps-launcher.jar;D:/Program Files/Java/jdk1.8.0_161/lib/tools.jar;D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/optimizedFileManager.jar"
java.property.java.endorsed.dirs=""""
java.property.java.ext.dirs="D:\Program Files\Java\jdk1.8.0_161\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext"
java.property.java.home="D:\Program Files\Java\jdk1.8.0_161\jre"
...

列出当前运行的 java 进程可以执行的操作

命令:jcmd <pid> help

$ jcmd 22912 help
22912:
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
GC.rotate_log
Thread.print
GC.class_stats
GC.class_histogram
GC.heap_dump
GC.run_finalization
GC.run
VM.uptime
VM.flags
VM.system_properties
VM.command_line
VM.version
help

查看具体命令选项

如果想查看命令选项,比如想查看 JFR.dump 命令选项,可以通过如下命令: jcmd <pid> help JFR.dump

$ jcmd 22912 help JFR.dump
22912:
JFR.dump
Copies contents of a JFR recording to file. Either the name or the recording id must be specified.

Impact: Low

Permission: java.lang.management.ManagementPermission(monitor)

Syntax : JFR.dump [options]

Options: (options must be specified using the <key> or <key>=<value> syntax)
        name : [optional] Recording name, e.g. \"My Recording\" (STRING, no default value)
        recording : [optional] Recording number, use JFR.check to list available recordings (JLONG, -1)
        filename :  Copy recording data to file, i.e \"C:\Users\user\My Recording.jfr\" (STRING, no default value)
        compress : [optional] GZip-compress "filename" destination (BOOLEAN, false)

JFR 相关命令

JFR 功能jmc.exe 工具飞行记录器的功能一样的。
要使用 JFR 相关的功能,必须使用 VM.unlock_commercial_features 参数取消锁定商业功能。

$ jcmd 22912 JFR.start
22912:
Java Flight Recorder not enabled.

Use VM.unlock_commercial_features to enable.

jmc.exe 显示的提示

  • 启动JFR

执行命令:

jcmd $PID JFR.start name=abc,duration=120s

 

  • Dump JFR

等待至少duration(本文设定120s)后,执行命令:

jcmd PID JFR.dump name=abc,duration=120s filename=abc.jfr

(注意,文件名必须为.jfr后缀

  • 检查JFR状态

执行命令:

jcmd $PID JFR.check name=abc,duration=120s
  • 停止JFR

执行命令:

jcmd $PID JFR.stop name=abc,duration=120s
  • JMC分析

切回开发机器,下载步骤3中,生成的abc.jfr打开jmc导入abc.jfr,即可进行可视化分析

VM.uptime

命令:jcmd <pid> VM.uptime

描述:查看 JVM 的启动时长。

$ jcmd 22912 VM.uptime
22912:
266194.602 s

 

GC.class_histogram

命令:jcmd <pid> GC.class_histogram

描述:查看系统中类统计信息。

$ jcmd 22912 GC.class_histogram
22912:

 num     #instances         #bytes  class name
----------------------------------------------
   1:           649       17000784  [B
   2:         19299        1799336  [C
   3:          7474         475320  [Ljava.lang.Object;
   4:         19116         458784  java.lang.String
   5:          3603         402048  java.lang.Class
   6:          6979         223328  java.util.concurrent.ConcurrentHashMap$Node
   7:          4405         140960  java.util.HashMap$Node
   8:          2372         113856  gnu.trove.THashMap
   9:          6447         103152  java.lang.Object
  10:          1428          98088  [I
  11:            47          58064  [Ljava.util.concurrent.ConcurrentHashMap$Node;

这里和jmap -histo pid的效果是一样的,可以查看每个类的实例数量占用空间大小

Thread.print

命令:jcmd <pid> Thread.print
描述:查看线程堆栈信息。

$ jcmd 22912 Thread.print
22912:
2019-05-13 13:23:42
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.161-b12 mixed mode):

"NettythreadDeathWatcher-2-1" #17 daemon prio=1 os_prio=-2 tid=0x00000000178f0800 nid=0x8190 waiting on condition [0x0000000018faf000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at io.netty.util.ThreadDeathWatcher$Watcher.run(ThreadDeathWatcher.java:152)
        at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
        at java.lang.Thread.run(Thread.java:748)

"DestroyJavaVM" #15 prio=5 os_prio=0 tid=0x00000000178f1800 nid=0x67d4 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JPS event loop" #10 prio=5 os_prio=0 tid=0x0000000016ab4800 nid=0x1a80 runnable [0x00000000170ae000]
   java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.WindowsSelectorImpl$SubSelector.poll0(Native Method)
        at sun.nio.ch.WindowsSelectorImpl$SubSelector.poll(WindowsSelectorImpl.java:296)
        at sun.nio.ch.WindowsSelectorImpl$SubSelector.access$400(WindowsSelectorImpl.java:278)
        at sun.nio.ch.WindowsSelectorImpl.doSelect(WindowsSelectorImpl.java:159)
        at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
        - locked <0x00000000d5419030> (a io.netty.channel.nio.SelectedSelectionKeySet)
        - locked <0x00000000d5419020> (a java.util.Collections$UnmodifiableSet)
        - locked <0x00000000d5418fa0> (a sun.nio.ch.WindowsSelectorImpl)
        at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
        at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:62)
        at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:752)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:408)
        at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
        at java.lang.Thread.run(Thread.java:748)

该命令同 jstack 命令。

GC.heap_dump

命令:jcmd <pid> GC.heap_dump FILE_NAME
描述:查看 JVM 的Heap Dump

$ jcmd  22912  GC.heap_dump  d:\dump.hprof
22912:
Heap dump file created

jmap命令

jmap -dump:format=b,file=heapdump.phrof pid 

效果一样导出的 dump 文件,可以使用MAT 或者 Visual VM 等工具进行分析(如果只指定文件名,默认会生成在启动 JVM 的目录里)。

VM.system_properties

命令:jcmd <pid> VM.system_properties
描述:查看 JVM 的属性信息。

$ jcmd 22912 VM.system_properties
22912:
#Mon May 13 13:28:38 CST 2019
java.vendor=Oracle Corporation
preload.project.path=D\:/WorkSpace/hqev/rbac
sun.java.launcher=SUN_STANDARD
sun.management.compiler=HotSpot 64-Bit Tiered Compilers
sun.nio.ch.bugLevel=
idea.config.path=D\:/Program Files/JetBrains/IntelliJIdeaConfig/config
idea.paths.selector=IntelliJIdea2018.1
kotlin.daemon.client.alive.path="C\:\\Users\\Victor.Zxy\\AppData\\Local\\Temp\\kotlin-idea-3229673183181290493-is-running"
os.name=Windows 10
sun.boot.class.path=D\:\\Program Files\\Java\\jdk1.8.0_161\\jre\\lib\\resources.jar;D\:\\Program Files\\Java\\jdk1.8.0_161\\jre\\lib\\rt.jar;D\:\\Program Files\\Java\\jdk1.8.0_161\\jre\\lib\\sunrsasign.jar;D\:\\Program Files\\Java\\jdk1.8.0_161\\jre\\lib\\jsse.jar;D\:\\Program Files\\Java\\jdk1.8.0_161\\jre\\lib\\jce.jar;D\:\\Program Files\\Java\\jdk1.8.0_161\\jre\\lib\\charsets.jar;D\:\\Program Files\\Java\\jdk1.8.0_161\\jre\\lib\\jfr.jar;D\:\\Program Files\\Java\\jdk1.8.0_161\\jre\\classes
sun.desktop=windows
idea.plugins.path=D\:/Program Files/JetBrains/IntelliJIdeaConfig/config/plugins
java.vm.specification.vendor=Oracle Corporation
java.runtime.version=1.8.0_161-b12
io.netty.serviceThreadPrefix=Netty
user.name=Victor.Zxy
kotlin.incremental.compilation=true
idea.home.path=D\:\\Program Files\\JetBrains\\IntelliJ IDEA 2018.1
user.language=zh
jdt.compiler.useSingleThread=true
sun.boot.library.path=D\:\\Program Files\\Java\\jdk1.8.0_161\\jre\\bin
java.version=1.8.0_161
user.timezone=Asia/Shanghai

VM.flags

命令:jcmd <pid> VM.flags
描述:查看 JVM 的启动参数。

$ jcmd 22912 VM.flags
22912:
-XX:CICompilerCount=3 -XX:InitialHeapSize=335544320 
-XX:MaxHeapSize=734003200 -XX:MaxNewSize=244318208 
-XX:MinHeapDeltaBytes=524288 -XX:NewSize=111673344 
-XX:OldSize=223870976 -XX:-PrintGC -XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps 
-XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC

VM.command_line

命令:jcmd <pid> VM.command_line
描述:查看 JVM 的启动命令行。

$ jcmd 22912 VM.command_line
22912:
VM Arguments:
jvm_args: -Xmx700m 
-Djava.awt.headless=true 
-Djava.endorsed.dirs="" 
-Djdt.compiler.useSingleThread=true 
-Dpreload.project.path=D:/WorkSpace/hqev/rbac 
-Dpreload.config.path=D:/Program Files/JetBrains/IntelliJIdeaConfig/config/options 
-Dcompile.parallel=false 
-Drebuild.on.dependency.change=true 
-Djava.net.preferIPv4Stack=true 
-Dio.netty.initialSeedUniquifier=8246017585702392224 
-Dfile.encoding=UTF-8 -Duser.language=zh 
-Duser.country=CN 
-Didea.paths.selector=IntelliJIdea2018.1 
-Didea.home.path=D:\Program Files\JetBrains\IntelliJ IDEA 2018.1 
-Didea.config.path=D:/Program Files/JetBrains/IntelliJIdeaConfig/config 
-Didea.plugins.path=D:/Program Files/JetBrains/IntelliJIdeaConfig/config/plugins 
-Djps.log.dir=D:/Program Files/JetBrains/IntelliJIdeaConfig/system/log/build-log 
-Djps.fallback.jdk.home=D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/jre64 
-Djps.fallback.jdk.version=1.8.0_152-release 
-Dio.netty.noUnsafe=true 
-Djava.io.tmpdir=D:/Program Files/JetBrains/IntelliJIdeaConfig/system/compile-server/rbac_aca57a50/_temp_ 
-Djps.backward.ref.index.builder=true 
-Dkotlin.incremental.compilation=true 
-Dkotlin.daemon.enabled 
-Dkotlin.daemon.client.alive.path="C:\Users\Victor.Zxy\AppData\Local\Temp\kotlin-idea-3229673183181290493-is-running"
java_command: org.jetbrains.jps.cmdline.Launcher 
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/jps-builders.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/annotations.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/trove4j.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/resources_en.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/protobuf-java-3.0.0.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/idea_rt.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/commons-logging-1.2.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/slf4j-api-1.7.10.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/asm-all.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/lz4-java-1.3.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/javac2.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/aether-1.1.0-all.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/httpcore-4.4.5.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/guava-21.0.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/jna.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/oro-2.0.8.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/jps-model.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/jps-builders-6.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/util.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/nanoxml-2.2.3.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/httpclient-4.5.2.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/jna-platform.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/forms_rt.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/aether-dependency-resolver.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/jgoodies-forms.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/snappy-in-java-0.5.1.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/maven-aether-provider-3.3.9-all.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/commons-cod
java_class_path (initial): 
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/jps-launcher.jar;
D:/Program Files/Java/jdk1.8.0_161/lib/tools.jar;
D:/Program Files/JetBrains/IntelliJ IDEA 2018.1/lib/optimizedFileManager.jar
Launcher Type: SUN_STANDARD

GC.run_finalization

命令:jcmd <pid> GC.run_finalization
描述: 对 JVM 执行 java.lang.System.runFinalization()

$ jcmd 22912 GC.run_finalization
22912:
Command executed successfully

执行一次finalization操作,相当于执行java.lang.System.runFinalization()

GC.run

命令:jcmd <pid> GC.run
描述:对 JVM 执行 java.lang.System.gc()

$ jcmd 22912 GC.run
22912:
Command executed successfully

告诉垃圾收集器,打算进行垃圾收集,而垃圾收集器进不进行收集是不确定的。

PerfCounter.print

命令:jcmd <pid> PerfCounter.print
描述:查看 JVM 性能相关的参数

$ jcmd 22912 PerfCounter.print
22912:
java.ci.totalTime=45606938
java.cls.loadedClasses=3363
java.cls.sharedLoadedClasses=0
java.cls.sharedUnloadedClasses=0
java.cls.unloadedClasses=9
...

VM.version

命令:jcmd <pid> VM.version
描述:查看目标jvm进程的版本信息

$ jcmd 22912 VM.version
22912:
Java HotSpot(TM) 64-Bit Server VM version 25.161-b12
JDK 8.0_161

系统层面的工具

linux下的工具使用

用NMT和pmap基本就就能搞清楚Java进程,为什么占了那些Virtual SizeRSSNMTNative Memory Tracking的缩写,是Java7U40引入的HotSpot新特性。 pmap,众所周知,就是Linux上用来看进程地址空间的

先说分析结果,在下面pmap的输出中,如下两条就包含了Java的Heap空间。

START               SIZE     RSS     PSS   DIRTY          SWAP PERM MAPPING
00000000d54aa000  92824K  92824K  92824K  92824K       0K  rw-p   [anon]
00000000daf50000  174784K 174784K 174784K 174784K       0K  rw-p   [anon]

从下面NMT的输出中,我们可以得出地址空间[0xd5a00000, 0xe5a00000]正是对应了Java的Heap。0xd5a00000正好位于上面pmap输出的第一条记录中,0xe5a00000正好位于上面pmap输出的第二条记录中。更详细的对应关系如下图所示,

  • C到E就是对应到Java Heap;
  • C到D就是对应到新生代(左边的S0C, S1C, EC, OCjstat -gc的输出);
  • D到E就是对应到老年代

下面我们分三步去分析清楚Java是如何占用这些底层操作系统的内存的。(以下讨论,假定要分析的Java程序的进程号为14179,其实这个进程号就是一个Tomcat运行实例,上面运行着我的应用。)

NMT的输出

首先,你要在Java启动项中,加入启动项: -XX:NativeMemoryTracking=detail 然后,重新启动Java程序。执行如下命令: 

jcmd 14179 VM.native_memory detail
14179:
Native Memory Tracking:
Total: reserved=653853KB, committed=439409KB
-           Java Heap (reserved=262144KB, committed=262144KB)
                            (mmap: reserved=262144KB, committed=262144KB) 

-          Class (reserved=82517KB, committed=81725KB)
                            (classes #17828)
                            (malloc=1317KB #26910) 
                            (mmap: reserved=81200KB, committed=80408KB) 

-           Thread (reserved=20559KB, committed=20559KB)
                            (thread #58)
                            (stack: reserved=20388KB, committed=20388KB)
                            (malloc=102KB #292) 
                            (arena=69KB #114)

-            Code (reserved=255309KB, committed=41657KB)
                            (malloc=5709KB #11730) 
                            (mmap: reserved=249600KB, committed=35948KB) 

-             GC (reserved=1658KB, committed=1658KB)
                            (malloc=798KB #676) 
                            (mmap: reserved=860KB, committed=860KB) 

-              Compiler (reserved=130KB, committed=130KB)
                            (malloc=31KB #357) 
                            (arena=99KB #3)

-             Internal (reserved=5039KB, committed=5039KB)
                            (malloc=5007KB #20850) 
                            (mmap: reserved=32KB, committed=32KB) 

-            Symbol (reserved=18402KB, committed=18402KB)
                            (malloc=14972KB #221052) 
                            (arena=3430KB #1)

-    Native Memory Tracking (reserved=2269KB, committed=2269KB)
                            (malloc=53KB #1597) 
                            (tracking overhead=2216KB)


-               Arena Chunk (reserved=187KB, committed=187KB)
                            (malloc=187KB) 

-                   Unknown (reserved=5640KB, committed=5640KB)
                            (mmap: reserved=5640KB, committed=5640KB) 
 . . .
Virtual memory map:

[0xceb00000 - 0xcec00000] reserved 1024KB for Class from
[0xced00000 - 0xcee00000] reserved 1024KB for Class from
. . .
[0xcf85e000 - 0xcf8af000] reserved and committed 324KB for Thread Stack from
[0xd4eaf000 - 0xd4f00000] reserved and committed 324KB for Thread Stack from
    [0xf687866e] Thread::record_stack_base_and_size()+0x1be
    [0xf68818bf] JavaThread::run()+0x2f
    [0xf67541f9] java_start(Thread*)+0x119
    [0xf7606395] start_thread+0xd5
[0xd5a00000 - 0xe5a00000] reserved 262144KB for Java Heap from
. . .
[0xe5e00000 - 0xf4e00000] reserved 245760KB for Code from
[0xf737f000 - 0xf7400000] reserved 516KB for GC from
[0xf745d000 - 0xf747d000] reserved 128KB for Unknown from
[0xf7700000 - 0xf7751000] reserved and committed 324KB for Thread Stack from
[0xf7762000 - 0xf776a000] reserved and committed 32KB for Internal from

上面的输出也就两大部分:TotalVirtual Memory Map.

Total部分就是Java进程所使用的本地内存大小的一个分布Heap用了多少,所有的Class用了多少。其中,最重要的一个就是Heap大小,此处它的Reserved值262144KB, 其实也就是256MB, 因为该Java启动参数最大堆设为了256M:-Xmx256M

Virtual Memory Map部分就是细节了,也就是Java进程地址空间每一段用来干什么的,大小是多少。这些进程空间段按照用途分可以分为以下几种:

  • Reserved for Class (总共有76段)

例如:[0xceb00000 - 0xcec00000] reserved 1024KB for Class from
大部分的为Class分配的进程空间都是1024KB的。

  • Reserved for Heap ( 总共只有1段)

例如:[0xd5a00000 - 0xe5a00000] reserved 262144KB for Java Heap from
简单演算一下:0xe5a00000-0xd5a00000=0x10000000=pow(2, 28)。很明显2的28方个比特,就是256MB.

  • Reserved for Internal (总共只有1段)

例如:[0xf7762000 - 0xf776a000] reserved and committed 32KB for Internal from

  • Reserved for Thread Stack(总共有57段)

例如:[0xcf85e000 - 0xcf8af000] reserved and committed 324KB for Thread Stack from
从输出看,大部分的 Stack的地址空间都是324KB的,还有不少部分是516KB的。

  • Reserved for Code( 总共有2段 )

例如:[0xe5e00000 - 0xf4e00000] reserved 245760KB for Code from
这个地方,用了好大的进程空间。后面,我们会在pmap的输出中找到它。它用了很大的Virtual Address Space, 但是RSS却相对比较小。

  • Reserved for Unknown( 总共有4 段)

例如: [0xf745d000 - 0xf747d000] reserved 128KB for Unknown from

  • Reserved for GC (总共有2段)

例如: [0xf737f000 - 0xf7400000] reserved 516KB for GC from

pmap

查看进程的内存映像信息(report memory map of a process)
pmap [options] PID [PID ...]

选项
Options:
 -x, --extended              show details //显示扩展格式
 -X                          show even more details
            WARNING: format changes according to /proc/PID/smaps
 -XX                         show everything the kernel provides
 -c, --read-rc               read the default rc
 -C, --read-rc-from=<file>   read the rc from file
 -n, --create-rc             create new default rc
 -N, --create-rc-to=<file>   create new rc to file
            NOTE: pid arguments are not allowed with -n, -N
 -d, --device                show the device format //显示设备格式
 -q, --quiet                 do not display header and footer //不显示头尾行
 -p, --show-path             show path in the mapping
 -A, --range=<low>[,<high>]  limit results to the given range

 -h, --help     display this help and exit
 -V, --version  output version information and exit //显示版本

扩展格式和设备格式域:
  Address:  start address of map  映像起始地址
  Kbytes:  size of map in kilobytes  映像大小(KB)
  RSS:  resident set size in kilobytes  驻留集大小(保留内存大小)(KB)
  Dirty:  dirty pages (both shared and private) in kilobytes  脏页大小(KB)
  Mode:  permissions on map 映像权限: r=read, w=write, x=execute, s=shared, p=private (copy on write)  
  Mapping:  file backing the map , or '[ anon ]' for allocated memory, or '[ stack ]' for the program stack.  占用内存的文件,[anon]为已分配内存,[stack]程序堆栈
  Offset:  offset into the file  文件偏移
  Device:  device name (major:minor)  设备名

#  pmap -x 5002
5002:   /opt/jvm/java8/bin/java -Dorg.mortbay.util.URI.charset=UTF-8 -Xmx6008331k -Xms6008331k -XX:MaxPermSize=512m -Djava.library.path=/opt/app/../libswt/linux/x86_64/ ......
e.enc
Address           Kbytes     RSS   Dirty Mode  Mapping
0000000000400000       4       4       0 r-x-- java
0000000000600000       4       4       4 rw--- java
000000000120d000     132      24      24 rw---   [ anon ]
0000000651400000 6015488  573320  573320 rw---   [ anon ]
......
......
00007fffa449f000     132      44      44 rw---   [ stack ]
00007fffa45fe000       8       4       0 r-x--   [ anon ]
ffffffffff600000       4       0       0 r-x--   [ anon ]
---------------- ------- ------- ------- 
total kB         12052840  856168  806504
#  pmap -d pid
5002:   /opt/jvm/java8/bin/java -Dorg.mortbay.util.URI.charset=UTF-8 -Xmx6008331k -Xms6008331k -XX:MaxPermSize=512m -Djava.library.path=/opt/app/../libswt/linux/x86_64/ ......
Address           Kbytes Mode  Offset           Device    Mapping
0000000000400000       4 r-x-- 0000000000000000 0fd:00000 java
0000000000600000       4 rw--- 0000000000000000 0fd:00000 java
000000000120d000     132 rw--- 0000000000000000 000:00000   [ anon ]
......
......
00007fffa449f000     132 rw--- 0000000000000000 000:00000   [ stack ]
00007fffa45fe000       8 r-x-- 0000000000000000 000:00000   [ anon ]
ffffffffff600000       4 r-x-- 0000000000000000 000:00000   [ anon ]
mapped: 12014504K    writeable/private: 6589148K    shared: 38336K

最后一行的值
mapped:表示该进程映射虚拟地址空间大小,也就是该进程预先分配虚拟内存大小,即ps出的vsz
writeable/private:表示进程所占用的私有地址空间大小,也就是该进程实际使用的内存大小      
shared:表示进程其他进程共享的内存大小

只显示最后一行:

#  pmap -d 5002 | tail -1
mapped: 12014504K    writeable/private: 6589148K    shared: 38336K

循环显示最后一行,试试监控进程内存占用情况:

#  while true; do pmap -d 5002 | tail -1; sleep 3; done

使用命令行: pmap -p PID, 我们就可以得到对应进程的VSS&RSS信息。
pmap输出的中,我们把其中我们比较关心的部分列在下面:

# pmap -p 5002
START               SIZE     RSS     PSS   DIRTY    SWAP PERM MAPPING
0000000008048000      4K      4K      4K      0K      0K r-xp /usr/java/jre1.8.0_65/bin/java
0000000008049000      4K      4K      4K      4K      0K rw-p /usr/java/jre1.8.0_65/bin/java
000000000804a000  74348K  71052K  71052K  71052K      0K rw-p [heap]
…
00000000ced00000   1024K    976K    976K    976K      0K rw-p [anon]
…
00000000d4eaf000     12K      0K      0K      0K      0K ---p [anon]
00000000d4eb2000    312K     28K     28K     28K      0K rwxp [stack:21151]
00000000d4f00000   1024K   1024K   1024K   1024K      0K rw-p [anon]
00000000d5000000     32K     32K     32K      0K      0K r-xp /usr/java/jre1.8.0_65/jre/lib/i386/libmanagement.so
00000000d5008000      4K      4K      4K      4K      0K rw-p /usr/java/jre1.8.0_65/jre/lib/i386/libmanagement.so
00000000d500d000    324K     24K     24K     24K      0K rwxp [stack:18608]
00000000d505e000   4376K   4376K   4376K   4376K      0K rw-p [anon]
00000000d54a4000     24K      0K      0K      0K      0K ---p [anon]
00000000d54aa000  92824K  92824K  92824K  92824K      0K rw-p [anon]
00000000daf50000 174784K 174784K 174784K 174784K      0K rw-p [anon]
00000000e5a40000    544K    544K    544K    544K      0K rw-p [anon]
00000000e5ac8000   3296K      0K      0K      0K      0K ---p [anon]
00000000e5e00000  34656K  34300K  34300K  34300K      0K rwxp [anon]
00000000e7fd8000 211104K      0K      0K      0K      0K ---p [anon]
00000000f4e00000    100K     60K     60K      0K      0K r-xp /usr/java/jre1.8.0_65/jre/lib/i386/libzip.so
00000000f4e19000      4K      4K      4K      4K      0K rw-p /usr/java/jre1.8.0_65/jre/lib/i386/libzip.so
00000000f4e5e000    648K     68K     68K     68K      0K rwxp [stack:18331]
00000000f4f00000   1024K   1024K   1024K   1024K      0K rw-p [anon]
…
Total:           735324K 482832K 479435K 462244K      0K

我们对几个重要部分的pmap输出一一作出分析,

  • 000000000804a000: 该部分是Java进程Heap区,此处的Heap,指的不是Java那种特殊的Heap, 还是一个任何C/C++内存区域中Heap区。VSS和RSS差不多,都在70M上下
  • 00000000ced00000: 该区域就是用来,存放class的。在NMT输出中,可以找到对应项。Mapping那一栏是[anon], 因为pmap并不知道这部分区域,是干什么用的,而直有JVM自己知道,所以, NMT的输出可以看出该内存区域的用处。
  • 00000000d4eaf000 00000000d4eb2000: 这两部分合起来就是一个324K大小Java Thread StackNTM输出中可以找到对应项
  • 00000000d54aa000, 00000000daf50000: 这两部分就非常重要的。它对应就是我们Java意义上的堆的那一部分。简单地讲,- 00000000daf50000那一块就是老年代(Java内存分布,分析要以垃圾收集算法为前提)。00000000d54aa000这一部分,包含了新生代

jstat -gc的输出

$jstat -gc 14179
Picked up JAVA_TOOL_OPTIONS: -XX:-UseLargePages
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
8704.0 8704.0 2435.5  0.0   69952.0  29138.2   174784.0   146972.4  83736.0 82674.8  0.0    0.0      740    9.341  81      3.713   13.054

这样,就可以把pmapRSSJavaHeap联系和对应起来。(注:笔者测试环境是Java 8, 所以你在jstat输出中会看到MC和MU)。

windows下的工具使用

任务管理器

任务管理器(Ctrl+Alt+Del将其打开,就可以看见你想看见的信息了)

perfmon性能监控工具

可以用于监视CPU使用率、内存使用率、硬盘读写速度、网络速度等。Perfmon提供了图表化的系统性能实时监视器、性能日志和警报管理,系统的性能日志可定义为二进制文件、文本文件、SQLSERVER表记录等方式,可以很方便地使用第三方工具进行性能分析。perfmon.exe 文件位于C:WindowsSystem32目录下。
快捷键 win+ R打开运行,输入 perfmon :

打开性能监控工具

点击绿色的+号添加计数器Process表示进程Thread表示线程
下面我们将对QQ的部分线程进行监控,选中实例点击添加,确定。

生成报告。可以看到线程ID,占用CPU的多少。

Process Explorer

Sysinternals开发的Windows系统和应用程序监视工具,目前已并入微软旗下。不仅结合了Filemon(文件监视器)Regmon(注册表监视器)两个工具的功能,还增加了多项重要的增强功能。包括稳定性性能改进强大的过滤选项、修正的进程树对话框(增加了进程存活时间图表)、可根据点击位置变换的右击菜单过滤条目集成带源代码存储的堆栈跟踪对话框、更快的堆栈跟踪、可在 64位 Windows 上加载 32位 日志文件的能力监视映像(DLL和内核模式驱动程序)加载、系统引导记录所有操作等。

pslist命令行工具

pslist是一个windows下的命令行工具。

  • 基本用法如下:

pslist [-d] [-m] [-x] [-t] [-s [n] [-r n] [name|pid]
-d:显示线程详细信息。
-m:显示内存详细信息。
-x:显示进程内存线程信息。
-t:显示进程间父子关系。
-s[n]:进入监控模式。n指定程序运行时间,使用ESC键退出。
-r n:指定监控模式下的刷新时间,单位为秒。
name:指定监控的进程名称,pslist将监控所有以给定名字开头进程
-e:使用精确匹配,打开这个开关,pslist将只监控name参数指定的进程。
pid:指定进程ID

  • 显示的栏位:

Pri:优先级
Thd:线程数
Hnd:句柄数
VM:虚拟内存
WS:工作集
Priv:专用虚拟内存
Priv Pk:专用虚拟内存峰值
Faults:页面错误
NonP:非页面缓冲池
ge:页面缓冲池
Cswtch:上下文切换

  • pslist是用命令行查看线程
pslist -t
Name                             Pid Pri Thd  Hnd      VM      WS    Priv
Idle                               0   0   2    0       0      28       0
  System                           4   8  69 1222    1824     308       0
    smss                         832  11   3   20    3748     408     172
      csrss                      900  13  12  807   72428   16152    2568
      winlogon                   924  13  21  516   61272    4704    8536
        services                 968   9  15  280   22556    4516    1868
          avp                    256   8  36 7185  190528   22332   50308
explorer                        2060   8  16  575  122880   13400   17752
  msnmsgr                       1604   8  33  778  222560   19240   32792
  cmd                           3680   8   1   31   31084    3004    2164
    pslist                      5476  13   2   91   30500    2744    1236
  notepad                       4276   8   1   45   33692    3956    1344
  IEXPLORE                      5184   8  61 2143  403392   31236  105436
  eclipse                       6088   8   1   33   29884    3184     960
    javaw                       4484   8  40 1197  729124  139424  193496
      javaw                     4252   8  11(十一个线程)  310  187820    8080   13908

  • 查看进程中的线程
pslist -dmx 4252
Name                Pid      VM      WS    Priv Priv Pk   Faults   NonP Page
javaw              4252  202224   21848   23968   24476     7927      4   47
 Tid Pri    Cswtch            State     User Time   Kernel Time   Elapsed Time
5428   8      2617     Wait:UserReq  0:00:01.312   0:00:00.515    0:06:41.625
5312  15       614     Wait:UserReq  0:00:00.078   0:00:00.000    0:06:41.484
1380  15         7     Wait:UserReq  0:00:00.000   0:00:00.000    0:06:41.468
2012  10         7     Wait:UserReq  0:00:00.000   0:00:00.000    0:06:41.468
3876   9      1037     Wait:UserReq  0:00:00.046   0:00:00.187    0:06:41.187
5884   9        65     Wait:UserReq  0:00:00.000   0:00:00.015    0:06:41.187
4444  10       236     Wait:UserReq  0:00:00.000   0:00:00.015    0:06:41.171
4564  15        12     Wait:UserReq  0:00:00.000   0:00:00.000    0:06:40.953
4644  15       270     Wait:UserReq  0:00:00.234   0:00:00.015    0:06:40.953
4292   8         5     Wait:UserReq  0:00:00.000   0:00:00.000    0:06:40.953
5964  15      6422   Wait:DelayExec  0:00:00.000   0:00:00.000    0:06:40.937

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿啄debugIT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值