javaone
Oracle HotSpot JDK提供jcmd,这是一种命令行工具,旨在向后兼容和向前适应于Java的未来版本。 它旨在以标准化方式支持新SDK附带的新工具和功能。 下面的屏幕快照显示了它用于大多数基本的类似jps的功能(Larsen几乎像我刚才提到的那样提到jps ,并将jcmd
称为“类似jps
但功能更强大”)。
如上图所示, jcmd
可以像jps
一样使用。
Larsen显示了jcmd
命令的一些方便功能。 他有一些小的Java示例应用程序帮助他演示了jcmd。 出于我的目的,我在机器的一个终端上运行jconsole
,然后针对运行jconsole
JVM运行jcmd
命令。 下一个屏幕快照显示了基本(无参数) jcmd
调用如何提供有关该JConsole进程的信息。
jcmd
支持按进程ID(pid)或按进程名称针对JVM进程执行。 下一个屏幕快照显示了按该名称针对JConsole进程运行jcmd
并传递了该命令help
查看可以针对该特定进程运行哪些选项。 请注意,我尝试对'dustin'(没有现有进程)运行此操作未成功,以证明jcmd
确实显示了可用于运行进程的选项。
从上一个屏幕快照中展示的功能是从Oracle JDK jcmd
现有命令行工具迁移到jcmd
的最引人注目的原因之一。 此图显示jcmd
如何在每个进程的基础上提供可用选项的列表,从而在支持支持不同/新命令的Java的过去版本或将来版本方面提供了最大的灵活性。
就像jcmd <pid> help
(或用进程名称替换pid)列出了jcmd
针对特定的JVM进程运行的可用操作一样,该相同的帮助机制也可以针对任何这些列出的特定命令运行[使用语法jcmd <pid> <command_name> help
(或使用进程名称代替pid)],尽管我无法在Windows计算机上使其正常工作。
下图显示了针对该JVM进程实际运行该命令,而不是简单地寻求帮助。
在紧接上方的两个屏幕快照中,我针对pid(而不是进程名称)运行了jcmd
,只是为了表明它既可用于进程ID也可用于名称。 下一个屏幕快照显示了对JVM进程执行jcmd
以从JVM进程获取VM标志和命令行选项(此JConsole进程实例的pid为3556)。
对支持的JVM进程运行jcmd
的Thread.print
命令可以轻松地查看目标JVM的线程。 以下输出是通过对运行中的JConsole进程运行jcmd JConsole Thread.print
生成的。
3556:
2012-10-04 23:39:36
Full thread dump Java HotSpot(TM) Client VM (23.2-b09 mixed mode, sharing):
'TimerQueue' daemon prio=6 tid=0x024bf000 nid=0x1194 waiting on condition [0x069af000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x23cf2db0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at java.util.concurrent.DelayQueue.take(DelayQueue.java:209)
at javax.swing.TimerQueue.run(TimerQueue.java:171)
at java.lang.Thread.run(Thread.java:722)
'DestroyJavaVM' prio=6 tid=0x024be400 nid=0x1460 waiting on condition [0x00000000]
java.lang.Thread.State: RUNNABLE
'AWT-EventQueue-0' prio=6 tid=0x024bdc00 nid=0x169c waiting on condition [0x0525f000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x291a90b0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at java.awt.EventQueue.getNextEvent(EventQueue.java:521)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:213)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:163)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:147)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:139)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:97)
'Thread-2' prio=6 tid=0x024bd800 nid=0x4a8 in Object.wait() [0x04bef000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x2917ed80> (a java.io.PipedInputStream)
at java.io.PipedInputStream.read(PipedInputStream.java:327)
- locked <0x2917ed80> (a java.io.PipedInputStream)
at java.io.PipedInputStream.read(PipedInputStream.java:378)
- locked <0x2917ed80> (a java.io.PipedInputStream)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
- locked <0x29184e28> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:154)
at java.io.BufferedReader.readLine(BufferedReader.java:317)
- locked <0x29184e28> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:382)
at sun.tools.jconsole.OutputViewer$PipeListener.run(OutputViewer.java:109)
'Thread-1' prio=6 tid=0x024bd000 nid=0x17dc in Object.wait() [0x047af000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x29184ee8> (a java.io.PipedInputStream)
at java.io.PipedInputStream.read(PipedInputStream.java:327)
- locked <0x29184ee8> (a java.io.PipedInputStream)
at java.io.PipedInputStream.read(PipedInputStream.java:378)
- locked <0x29184ee8> (a java.io.PipedInputStream)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
- locked <0x2918af80> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:154)
at java.io.BufferedReader.readLine(BufferedReader.java:317)
- locked <0x2918af80> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:382)
at sun.tools.jconsole.OutputViewer$PipeListener.run(OutputViewer.java:109)
'AWT-Windows' daemon prio=6 tid=0x024bc800 nid=0x16e4 runnable [0x0491f000]
java.lang.Thread.State: RUNNABLE
at sun.awt.windows.WToolkit.eventLoop(Native Method)
at sun.awt.windows.WToolkit.run(WToolkit.java:299)
at java.lang.Thread.run(Thread.java:722)
'AWT-Shutdown' prio=6 tid=0x024bc400 nid=0x157c in Object.wait() [0x04c6f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x2918b098> (a java.lang.Object)
at java.lang.Object.wait(Object.java:503)
at sun.awt.AWTAutoShutdown.run(AWTAutoShutdown.java:287)
- locked <0x2918b098> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:722)
'Java2D Disposer' daemon prio=10 tid=0x024bbc00 nid=0x3b8 in Object.wait() [0x0482f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x2918b128> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
- locked <0x2918b128> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
at sun.java2d.Disposer.run(Disposer.java:145)
at java.lang.Thread.run(Thread.java:722)
'Service Thread' daemon prio=6 tid=0x024bb800 nid=0x1260 runnable [0x00000000]
java.lang.Thread.State: RUNNABLE
'C1 CompilerThread0' daemon prio=10 tid=0x024c6400 nid=0x120c waiting on condition [0x00000000]
java.lang.Thread.State: RUNNABLE
'Attach Listener' daemon prio=10 tid=0x024bb000 nid=0x1278 waiting on condition [0x00000000]
java.lang.Thread.State: RUNNABLE
'Signal Dispatcher' daemon prio=10 tid=0x024bac00 nid=0xe3c runnable [0x00000000]
java.lang.Thread.State: RUNNABLE
'Finalizer' daemon prio=8 tid=0x024a9c00 nid=0x15c4 in Object.wait() [0x046df000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x2918b358> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
- locked <0x2918b358> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:177)
'Reference Handler' daemon prio=10 tid=0x024a4c00 nid=0xe40 in Object.wait() [0x0475f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x2917e9c0> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:503)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
- locked <0x2917e9c0> (a java.lang.ref.Reference$Lock)
'VM Thread' prio=10 tid=0x024a3800 nid=0x164c runnable
'VM Periodic Task Thread' prio=10 tid=0x024e7c00 nid=0xcf0 waiting on condition
JNI global references: 563
Larsen展示了如何使用jcmd
提供的线程信息来解决死锁。
Larsen显示了使用jcmd
从正在运行的JVM进程中获取类直方图。 使用命令jcmd <pid> GC.class_histogram
完成此操作。 接下来显示其输出的一小部分(这次JConsole进程的pid为4080)。
4080:
num #instances #bytes class name
----------------------------------------------
1: 1730 3022728 [I
2: 5579 638168
3: 5579 447072
4: 645 340288
5: 4030 337448 [C
6: 645 317472
7: 602 218704
8: 942 167280 [B
9: 826 97720 java.lang.Class
10: 3662 87888 java.lang.String
11: 2486 79552 javax.swing.text.html.parser.ContentModel
12: 3220 77280 java.util.Hashtable$Entry
13: 1180 67168 [S
14: 2503 60072 java.util.HashMap$Entry
15: 181 59368
16: 971 43584 [Ljava.lang.Object;
17: 1053 41160 [[I
18: 206 29040 [Ljava.util.HashMap$Entry;
19: 111 27880 [Ljava.util.Hashtable$Entry;
20: 781 18744 java.util.concurrent.ConcurrentHashMap$HashEntry
21: 1069 17104 java.lang.Integer
22: 213 9816 [Ljava.util.concurrent.ConcurrentHashMap$HashEntry;
23: 202 9696 java.util.HashMap
24: 201 9280 [Ljava.lang.String;
25: 24 8416 [[I
Larsen还演示了jstat
及其一些有用的功能。 他演示了jstat -gcnew (新一代行为), jstat -precompilation (编译方法统计信息)和jstat -options (显示选项)的用法 。
在演示过程中,Larsen需要将十进制数(pid?) 转换为其十六进制表示形式,以便将其与另一个工具的输出进行比较。 他使用了方便的printf '%x\n' <pid>
命令来获取printf '%x\n' <pid>
的十六进制表示形式。
Larsen演示了如何使用VisualVM比较两个堆转储并浏览一个堆转储 。 他还演示了VisualVM Profiler 。
Larsen从先前介绍的旨在运行JVM的工具转变为可用于分析JVM核心文件的工具。 他返回jstack
分析核心文件的内容。
Larsen谈到了通过JMX以及jconsole
和jvisualvm
类的工具远程访问JVM信息。 他演示了jcmd
也可以用于启动JMX jcmd
: ManagementServer.start
“带有大量参数”。 Larsen认为,如果今天实现,VisualVM和JConsole将使用ManagementServer.start
而不是Attach API。
jstat
也可以通过使用jstatd远程连接到守护程序。 没有使用jstatd
加密或身份验证。
jps和jcmd使用“每个JVM的知名文件”查找系统上正在运行的文件:/ hsperfdata_ <user> / <pod>该文件在JVM启动时创建,在JVM关闭时删除。 未使用的先前文件会在启动时被删除,因此jps和jcmd(作为Java程序本身)将清除这些旧文件。
Attach API “允许发送“命令”以在JVM中执行”,但仅适用于本地计算机以及当前/相同用户。 这就是jcmd
和jstack
用途。 然后,Larsen继续说明了将Attach API用于Linux / BSD / Solaris(使用临时文件创建)与Windows(使用代码注入)的不同机制。 我在Groovy,JMX和Attach API中使用了Attach API 。
诊断命令是“ JVM内部的帮助程序”,可产生“文本输出”。 可以通过jcmd
实用程序(很快通过JMX)执行它们。 他们每个人都有一个自我描述的工具: jcmd PerfCounter.print
可以查看原始内容。
Larsen显示了一张比较“与JVM通讯”方法的信息表: attach , jvmstat , JMX , jstatd和Serviceability Agent (SA)。 SA“应被用作最后的手段(通常用于挂起的JVM)”,并使用“调试器读取信息”。
Larsen转而谈论未来的工具。 他从Java Flight Recorder的介绍开始了演示的这一部分。 Java Flight Recorder是“ JVM内置的探查器和跟踪器”,具有“低开销”并且“始终处于打开状态”。 其他即将推出的工具包括Java Mission Control (“图形工具,提供非常详细的运行时监视详细信息”),针对jcmd
更多诊断命令(“出于各种原因最终替换jstack,jmap,jinfo”), JMX 2.0 (“我们正在了解的东西”)再次;它是在很久以前开始的),改进了JVM的日志记录(JVM增强建议[JEP] 158 )和Java发现协议(为此即将推出JEP)。
提出的一个问题是,是否可以像在JConsole中那样在VisualVM中看到MBean。 正如我在上发表的文章 ,有一个VisualVM插件可以做到这一点。
尽管我对Oracle HotSpot JDK命令行工具感到有些满意,但我并不熟悉jcmd
并赞赏Larsen对它的介绍。 在此过程中,我还学到了其他一些东西。 我唯一的抱怨是Larsen的演讲(尤其是演示)是如此Swift,内容如此丰富,我希望我能再次看到它。
可以在http://www.oracle.com/javaone/lad-en/session-presentations/corejava/22260-enok-1439100.pdf上找到相关的(但较旧的)具有相同内容的演示文稿。
参考: JavaOne 2012:在我们的JCG合作伙伴 Dustin Marx的Inspired by Actual Events博客上诊断JVM上的应用程序 。
翻译自: https://www.javacodegeeks.com/2012/10/javaone-2012-diagnosing-your.html
javaone