ps命令,打印所有正在运行的进程的相关信息。 JDK中的jps命令,打印所有正在运行的Java进程的相关信息。
在默认情况下,jps的输出信息包括Java进程的进程ID以及主类名。可以通过追加参数,来打印额外的信息。 -l将打印模块名以及包名; -v将打印传递给JVM的参数(如-XX:UnlockExperimentalVMOptions -XX:UseZGC); -m将打印传递给主类的参数。
1 2 3 $ jps -mlv 18331 org.example.Foo Hello World18332 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(JVM Statics Monitoring) jstat命令可用来打印目标Java进程的性能数据 。它包括多条子命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ jstat -options -class -compiler -gc -gccapacity -gccause -gcmetacapacity -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcutil -printcompilation # -class将打印类加载相关的数据。 # -compiler和-printcompilation将打印即时编译相关的数据。 # 以-gc为前缀的子命令,将打印垃圾回收相关的数据。
默认情况下,jstat只会打印一次性能数据。可以将它配置为每隔一段时间打印一次,直至目标Java进程终止,或者达到所配置的最大打印次数。
CMC垃圾回收器 1 2 3 4 5 6 7 8 9 # Usage: jstat -outputOptions [-t] [-hlines] VMID [interval [count]] $ 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垃圾回收器 1 2 3 4 5 6 7 $ 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时,JVM不再设置Eden区、Survivor区、老年代区的内存边界,而是将堆划分为若干个等长内存区域。每个内存区域都可以作为Eden区、Survivor区以及老年代区的任一种,并且可以在不同区域类型之间来回切换。
换句话说,逻辑上只有一个Survivor区。当需要迁移Survivor区中的数据时(Copying GC),只需要另外申请一个或多个内存区域,作为新的Survivor区。
因此JVM决定在使用G1 GC时,将所有Survivor内存区域的总容量以及已使用量存放至S1C和S1U中,而S0C和S0U则被设置为0。当发生垃圾回收时,JVM可能出现Survivor内存区域内的对象全被回收或者晋升的现象。在这种情况下,JVM会将这块内存区域回收,并标记为可分配的状态。这样子做的结果是,堆中可能完全没有Survivor内存区域,因而相应的S1C和S1U将会是0。
-t 将在每行数据之前打印目标Java进程的启动时间。
1 2 3 $ 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
S0C:第一个幸存区大小 S1C:第二个幸存区大小 S0U:第一个幸存区使用大小 S1U:第二个幸存区使用大小 EC:伊甸园区的大小 EU:伊甸园区的使用大小 OC:老年代大小 OU:老年代使用大小 MC:方法区大小 MU:方法取使用大小 CCSC:压缩类空间大小 CCSU:压缩类空间使用大小 YGC:年轻代垃圾回收次数 YGCT:年轻代垃圾回收消耗时间 FGC:老年代垃圾回收次数 FGCT:老年代垃圾回收消耗时间 GCT:垃圾回收消耗总时间
可以比较Java进程的启动弄时间以及总GC时间(GCT),或者两次测量的间隔时间以及总GC时间的增量,来得出GC时间占运行时间的比例。
如果该比例超过20%,则说明目前堆的压力较大;如果该比例超过90%,则说明堆里几乎没有可用空间,随时都可能抛出OOM异常。
jstat还可以用来判断是否出现内存泄漏。在长时间运行的Java程序中,可以运行jstat命令连续获取多行性能数据,并取这几行数据中OU列(即已占用的老年代内存)的最小值。
然后每个一段较长的时间重复一次上述操作,来获得多组OU最小值。如果这些值呈上涨趋势,则说明该Java程序的老年代内存已使用量在不断上涨,这意味着无法回收的对象在不断增加,因此很有可能存在内存泄漏。
CGC和CGCT分别代表并发GC Stop-The-World的次数和时间。
jmap jmap命令,可以分析JVM堆中的对象 。包括多条子命令:
1 2 3 4 -clstats 打印被加载类的信息 -finalizerinfo 打印所有待finalize的对象 -histo 统计各个类的实例数目以及占用内存,并按照内存使用量从多至少的顺序排列。(-histo:live只统计堆中的存活对象) -dump 导出JVM堆的快照。(-dump:live只保存堆中的存活对象)
通常利用jmap -dump:live,format=b,file=filename.bin命令,将堆中所有存活对象导出至一个文件之中。
format=b将使jmap导出与hprof(Java9中已被移除)、-XX:+HeapDumpAfterFullGC、-XX:+HeapDumpOnOutOfMemoryError格式一致的文件。这种格式的文件可以被其它GUI工具查看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $ 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只需要直接读取即可)
jmap(以及jinfo、jstack、jcmd)依赖于JVM的Attach API,因此只能监控本地Java进程。
一旦开启JVM参数DisableAttachMechanism(使用参数-XX:+DisableAttachMechanism),基于Attach API的命令将无法执行。 如果不想被其它进程监控,需要开启该参数。
jinfo(Java Configuration Info) jinfo命令可用来查看目标Java进程的参数 ,如传递给JVM的-X(输出中的jvm_args)、-XX参数(输出中的VM Flags),以及可在Java层面通过System.getProperty获取的-D参数(输出中的System Properties)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $ 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参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # 其他manageable虚拟机参数 $ 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 Stack Trace) jstack命令可以用来打印目标Java进程中各个线程的栈轨迹 ,以及这些线程所持有的锁 。
jstack的其中一个应用场景便是死锁检测。
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 $ 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 可以直接使用jcmd命令,替代前面除了jstat之外的所有命令。
jcmd复制了jstat的部分代码,并支持通过PerfCounter.print子命令来打印所有的Performance Counter,但是没有保留jstat的输出格式,也没有重复打印的功能。
总结
jps将打印所有正在运行的Java进程。
jstat允许用户查看目标Java进程的类加载、即时编译以及垃圾回收 相关的信息。它常用于检测垃圾回收问题以及内存泄漏问题。
jmap允许用户统计目标Java进程的堆 中存放的Java对象,并将它们导出成二进制文件。
jinfo将打印目标Java进程的配置参数,并能够改动其中manageable的参数。
jstack将打印目标Java进程中各个线程的栈 轨迹、线程状态、锁状况等信息。它还将自动检测死锁。
jcmd是一把瑞士军刀,可以用来实现除了jstat之外所有命令的功能。