JavaOne 2012:在JVM上诊断应用程序

值得参加Staffan Larsen (Oracle Java Serviceability Architect)的演讲“ 在JVM上诊断应用程序 ”(Hilton Plaza A / B),只是为了学习Oracle JVM 7随附的新jcmd命令行工具。该演示对我来说是“奖金”,这对我在JavaOne 2012星期三参加的上一届会议非常好。

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进程运行jcmdThread.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以及jconsolejvisualvm类的工具远程访问JVM信息。 他演示了jcmd也可以用于启动JMX jcmdManagementServer.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中执行”,但仅适用于本地计算机以及当前/相同用户。 这就是jcmdjstack用途。 然后,Larsen继续说明了将Attach API用于Linux / BSD / Solaris(使用临时文件创建)与Windows(使用代码注入)的不同机制。 我在Groovy,JMX和Attach API中使用了Attach API

诊断命令是“ JVM内部的帮助程序”,可产生“文本输出”。 可以通过jcmd实用程序(很快通过JMX)执行它们。 他们每个人都有一个自我描述的工具: jcmd PerfCounter.print可以查看原始内容。

Larsen显示了一张比较“与JVM通信”方法的信息表: attachjvmstatJMXjstatdServiceability 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上诊断您的应用程序

2012-10-11


翻译自: https://www.javacodegeeks.com/2012/10/javaone-2012-diagnosing-your.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值