总结,学习,发现问题,再总结,避免以后,在需要的时候抓狂!
jvm中的gc的参数解释
年轻代survivor(幸存区) | S0C | 年轻代中第一个survivor(幸存区)的容量(kb) | |
S1C | 年轻代中第二个survivor(幸存区)的容量(kb) | ||
已使用 | S0U | 年轻代中第一个survivor(幸存区)目前已使用空间(kb) | |
S1U | 年轻代中第二个survivor(幸存区)目前已使用空间(kb) | ||
年轻代中Eden(伊甸园) | EC | 年轻代中Eden(伊甸园)的容量(kb) | |
EU | 年轻代中Eden(伊甸园)目前已使用空间(kb) | ||
Old代 | OC | Old代的容量(kb) | |
OU | Old代目前已使用空间(kb) | ||
Perm(持久代) | PC | Perm(持久代)的容量(kb) | |
PU | Perm(持久代)目前已使用空间(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代 | 最小最大 | OGCMN | old代中初始化(最小)的大小(kb) |
OGCMX | old代的最大容量(kb) | ||
OGC | old代当前新生成的容量(kb) | ||
perm代容量 | PGCMN | perm代中初始化(最小)的大小(kb) | |
PGCMX | perm代的最大容量(kb) | ||
PGC | perm代当前新生成的容量(kb) | ||
年轻代已使用的 占当前容量百分比 |
survivor(幸存区) | S0 | 年轻代中第一个survivor(幸存区)已使用的占当前容量百分比 |
S1 | 年轻代中第二个survivor(幸存区)已使用的占当前容量百分比 | ||
E | 年轻代中Eden(伊甸园)已使用的占当前容量百分比 | ||
O | old代已使用的占当前容量百分比 | ||
P | perm代已使用的占当前容量百分比 | ||
年轻代最大容量(kb) | survivor(幸存区) | S0CMX | 年轻代中第一个survivor(幸存区)的最大容量(kb) |
S1CMX | 年轻代中第二个survivor(幸存区)的最大容量(kb) | ||
ECMX | 年轻代中Eden(伊甸园)的最大容量(kb) | ||
DSS | 当前需要survivor(幸存区)的容量(kb)(Eden区已满) | ||
次数限制 | TT | 持有次数限制 | |
MTT | 最大持有次数限制 |
所谓的Java层面的命令工具,就是Java虚拟机的监控及诊断工具。今将使用刚刚发布的 Java 11 版本的工具,进行演示 JDK 中用于监控及诊断工具。
你可能用过ps命令,打印所有正在运行的进程的相关信息。JDK 中的jps命令(帮助文档)沿用了同样的概念:它将打印所有正在运行的 Java 进程的相关信息。
在默认情况下,jps的输出信息,包括 Java 进程的进程 ID 以及主类名。我们还可以通过追加参数,来打印额外的信息。例如:
具体的示例如下所示:
$ 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 命令!它是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 可以显示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 内存区域的总容量,以及已使用量存放至 S1C 和 S1U 中,而 S0C 和 S0U 则被设置为 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 时间占运行时间的比例。
jstat,还可以用来判断是否出现内存泄漏。在长时间运行的 Java 程序中,我们可以运行jstat命令,连续获取多行性能数据,并取这几行数据中 OU 列(即已占用的老年代内存)的最小值。
然后,我们每隔一段较长的时间,重复一次上述操作,来获得多组 OU 最小值。如果这些值,呈上涨趋势,则说明该 Java 程序的老年代内存已使用量在不断上涨,这意味着,无法回收的对象,在不断增加,因此很有可能存在内存泄漏。
上面没有涉及的列(或者其他子命令的输出),你可以查阅帮助文档了解具体含义。
至于文档中漏掉的 CGC 和 CGCT,它们分别代表并发 GC Stop-The-World 的次数和时间。
通过 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命令可以显示:VM内存中三代(young,old,perm)对象的使用和占用大小。如:
[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 命令可以显示年轻代对象的信息。
[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 命令可以显示年轻代对象的信息及其占用量。
[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 命令可以显示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 命令可以显示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 命令可以显示加载class的数量,及所占空间等信息。
[root@fleapx ~]# jstat -class 5801
Loaded Bytes Unloaded Bytes Time
10924 20744.5 0 0.0 13.11
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 命令可以显示当前VM执行的信息。
[root@fleapx ~]# jstat -printcompilation 5801
Compiled Size Type Method
1453 13 1 java/util/concurrent/atomic/AtomicBoolean get
在这种情况下,我们便可以请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(以及接下来的jinfo、jstack和jcmd)依赖于 Java 虚拟机的Attach API,因此只能监控本地 Java 进程。
一旦开启 Java 虚拟机参数DisableAttachMechanism(即使用参数-XX:+DisableAttachMechanism),基于 Attach API 的命令,将无法执行。反过来说,如果你不想被其他进程监控,那么你需要开启该参数。
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命令(帮助文档)可以用来打印目标 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 …),而且还会分析出具体的死锁。
在JDK1.7以后,新增了一个命令行工具 jcmd。是一个多功能的工具,可以用它来导出堆、查看Java进程、导出线程信息、执行GC、还可以进行采样分析(jmc 工具的飞行记录器)。
你还可以直接使用jcmd命令(帮助文档),来替代前面除了jstat之外的所有命令。
至于jstat的功能,虽然jcmd复制了jstat的部分代码,并支持通过PerfCounter.print子命令,来打印所有的 Performance Counter,但是它没有保留jstat的输出格式,也没有重复打印的功能。
$ jcmd -help
Usage: jcmd
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
参数描述
- pid:接收诊断命令请求的进程ID。
- main class :接收诊断命令,请求的进程的main类。匹配进程时,main类名称中,包含指定子字符串的任何进程,均是匹配的。如果多个正在运行的Java进程,共享同一个main类,诊断命令请求,将会发送到所有的这些进程中。
- command:接收诊断命令,请求的进程的main类。匹配进程时,main类名称,中包含指定子字符串的任何进程,均是匹配的。如果多个正在运行的Java进程,共享同一个main类,诊断命令请求,将会发送到所有的这些进程中。
注意: 如果任何参数含有空格,你必须使用英文的单引号或双引号将其包围起来。
此外,你必须使用转义字符来,转移参数中的单引号或双引号,以阻止操作系统shell处理这些引用标记。当然,你也可以在参数两侧,加上单引号,然后在参数内,使用双引号(或者,在参数两侧加上双引号,在参数中使用单引号)。
- Perfcounter.print:打印目标Java进程上,可用的性能计数器。性能计数器的列表,可能会,随着Java进程的不同,而产生变化。
- -f file:从文件file中读取命令,然后在目标Java进程上调用这些命令。在file中,每个命令必须写在单独的一行。以"#"开头的行,会被忽略。当所有行的命令,被调用完毕后,或者读取到含有stop关键字的命令,将会终止对file的处理。
- -l:查看所有的进程列表信息。
- -h:查看帮助信息。(同 -help)
jcmd
jcmd -l
jps -m
#以上三个命令的效果是一样的
描述:查看 当前机器上所有的 jvm 进程信息。
命令:jcmd
描述:查看指定进程的性能统计信息。
$ 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"
...
命令:jcmd
$ 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
$ 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 or = 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 功能跟 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 显示的提示
执行命令:
jcmd $PID JFR.start name=abc,duration=120s
等待至少duration(本文设定120s)后,执行命令:
jcmd PID JFR.dump name=abc,duration=120s filename=abc.jfr
(注意,文件名必须为.jfr后缀)
执行命令:
jcmd $PID JFR.check name=abc,duration=120s
执行命令:
jcmd $PID JFR.stop name=abc,duration=120s
切回开发机器,下载步骤3中,生成的abc.jfr,打开jmc,导入abc.jfr,即可进行可视化分析
命令:jcmd
描述:查看 JVM 的启动时长。
$ jcmd 22912 VM.uptime
22912:
266194.602 s
命令:jcmd
描述:查看系统中类统计信息。
$ 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的效果是一样的,可以查看每个类的实例数量和占用空间大小。
命令:jcmd
描述:查看线程堆栈信息。
$ 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 命令。
命令:jcmd
描述:查看 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 的目录里)。
命令:jcmd
描述:查看 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
命令:jcmd
描述:查看 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
命令:jcmd
描述:查看 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
命令:jcmd
描述: 对 JVM 执行 java.lang.System.runFinalization()
$ jcmd 22912 GC.run_finalization
22912:
Command executed successfully
执行一次finalization操作,相当于执行java.lang.System.runFinalization()
命令:jcmd
描述:对 JVM 执行 java.lang.System.gc()
$ jcmd 22912 GC.run
22912:
Command executed successfully
告诉垃圾收集器,打算进行垃圾收集,而垃圾收集器进不进行收集是不确定的。
命令:jcmd
描述:查看 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
...
命令:jcmd
描述:查看目标jvm进程的版本信息。
$ jcmd 22912 VM.version
22912:
Java HotSpot(TM) 64-Bit Server VM version 25.161-b12
JDK 8.0_161
用NMT和pmap基本就就能搞清楚Java进程,为什么占了那些Virtual Size和RSS。 NMT是Native 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输出的第二条记录中。更详细的对应关系如下图所示,
下面我们分三步去分析清楚Java是如何占用这些底层操作系统的内存的。(以下讨论,假定要分析的Java程序的进程号为14179,其实这个进程号就是一个Tomcat运行实例,上面运行着我的应用。)
首先,你要在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
上面的输出也就两大部分:Total和Virtual Memory Map.
Total部分就是Java进程所使用的本地内存大小的一个分布: Heap用了多少,所有的Class用了多少。其中,最重要的一个就是Heap大小,此处它的Reserved值为262144KB, 其实也就是256MB, 因为该Java启动参数最大堆设为了256M:-Xmx256M。
Virtual Memory Map部分就是细节了,也就是Java进程的地址空间的每一段是用来干什么的,大小是多少。这些进程空间段按照用途分可以分为以下几种:
例如:[0xceb00000 - 0xcec00000] reserved 1024KB for Class from
大部分的为Class分配的进程空间都是1024KB的。
例如:[0xd5a00000 - 0xe5a00000] reserved 262144KB for Java Heap from
简单演算一下:0xe5a00000-0xd5a00000=0x10000000=pow(2, 28)。很明显2的28方个比特,就是256MB.
例如:[0xf7762000 - 0xf776a000] reserved and committed 32KB for Internal from
例如:[0xcf85e000 - 0xcf8af000] reserved and committed 324KB for Thread Stack from
从输出看,大部分的 Stack的地址空间都是324KB的,还有不少部分是516KB的。
例如:[0xe5e00000 - 0xf4e00000] reserved 245760KB for Code from
这个地方,用了好大的进程空间。后面,我们会在pmap的输出中找到它。它用了很大的Virtual Address Space, 但是RSS却相对比较小。
例如: [0xf745d000 - 0xf747d000] reserved 128KB for Unknown from
例如: [0xf737f000 - 0xf7400000] reserved 516KB for GC from
查看进程的内存映像信息(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=read the rc from file
-n, --create-rc create new default rc
-N, --create-rc-to=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=[, 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输出一一作出分析,
$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
这样,就可以把pmap的RSS和Java的Heap联系和对应起来。(注:笔者测试环境是Java 8, 所以你在jstat输出中会看到MC和MU)。
任务管理器(Ctrl+Alt+Del将其打开,就可以看见你想看见的信息了)
可以用于监视CPU使用率、内存使用率、硬盘读写速度、网络速度等。Perfmon提供了图表化的系统性能实时监视器、性能日志和警报管理,系统的性能日志可定义为二进制文件、文本文件、SQLSERVER表记录等方式,可以很方便地使用第三方工具进行性能分析。perfmon.exe 文件位于C:WindowsSystem32目录下。
快捷键 win+ R打开运行,输入 perfmon :
打开性能监控工具
点击绿色的+号添加计数器,Process表示进程,Thread表示线程。
下面我们将对QQ的部分线程进行监控,选中实例,点击添加,确定。
由Sysinternals开发的Windows系统和应用程序监视工具,目前已并入微软旗下。不仅结合了Filemon(文件监视器)和Regmon(注册表监视器)两个工具的功能,还增加了多项重要的增强功能。包括稳定性和性能改进、强大的过滤选项、修正的进程树对话框(增加了进程存活时间图表)、可根据点击位置变换的右击菜单,过滤条目、集成带源代码存储的堆栈跟踪对话框、更快的堆栈跟踪、可在 64位 Windows 上加载 32位 日志文件的能力、监视映像(DLL和内核模式驱动程序)加载、系统引导时记录所有操作等。
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 -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