您现在的位置是:网站首页> 内容页

JVM问题定位的瑞士军刀——JCMD

  • 亚搏在线登录
  • 2019-04-18
  • 135人已阅读
简介jcmd是jdk自带的一个神器,能够很方便的对java程序进行profiling。jcmd其实可以替代很多常用的工具,比如jstak,jmap。接下来让我们了解下什么是jcmd,jcm

jcmd是jdk自带的一个神器,能够很方便的对java程序进行profiling。jcmd其实可以替代很多常用的工具,比如jstak,jmap。接下来让我们了解下什么是jcmd,jcmd能够帮助我们定位什么问题?工作中我们如何使用jcmd。

从jdk7开始,jcmd就是jdk自带的一个工具。在正确配置了JDK的PATH的情况下,我们可以直接在命令行中执行jcmd:

[root@vincent-testing ~]# jcmd -hUsage: 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

利用jcmd,我们能做很多事情,比如,抓现场堆栈(dump threads),抓运行时堆内存(dump heap)采集GC日志,定位高CPU占用问题等。

列出机器上运行的JVM

在没有jcmd的时候,我们也有很多找出JVM进程的方法,比如:linux下通过ps,使用jdk自带的jps等。使用jcmd,我们同样可以做到:

[root@vincent-testing ~]# jcmd -l13714 sun.tools.jcmd.JCmd -l16324 org.rzo.yajsw.app.WrapperJVMMain9350 org.rzo.yajsw.app.WrapperJVMMain9815 org.apache.catalina.startup.Bootstrap start

通过jcmd -l可以列出所有的Java程序,其功能和jps其实是一样的。

操作指定的JVM进程

列出所有的JVM程序,找到你感兴趣的进程id,运行jcmd <PID> help,看看我们可以做些什么:

[root@vincent-testing ~]# jcmd 9350 help9350:The following commands are available:JFR.stopJFR.startJFR.dumpJFR.checkVM.native_memoryVM.check_commercial_featuresVM.unlock_commercial_featuresManagementAgent.stopManagementAgent.start_localManagementAgent.startGC.rotate_logThread.printGC.class_statsGC.class_histogramGC.heap_dumpGC.run_finalizationGC.runVM.uptimeVM.flagsVM.system_propertiesVM.command_lineVM.versionhelpFor more information about a specific command use "help <command>".

可以看到,jcmd给我们提供了很多的功能。这里我们先说几个简单的。

抓现场堆栈

相信大部分的Java程序员肯定都用过jstack,通过jstack可以dump当前JVM的线程堆栈。通过jstack <PID> Thread.print可以达到一样的目的:

➜ ~ jcmd 3426 Thread.print3426:2018-08-02 21:36:07Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode):"Attach Listener" #16 daemon prio=9 os_prio=31 tid=0x00007fd9a43d0000 nid=0xd07 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE"NettythreadDeathWatcher-2-1" #15 daemon prio=1 os_prio=31 tid=0x00007fd9a4ca1800 nid=0xa903 waiting on condition [0x0000700005924000] 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:144) at java.lang.Thread.run(Thread.java:748)"DestroyJavaVM" #14 prio=5 os_prio=31 tid=0x00007fd9a3b7f800 nid=0x1a03 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE.... other threads ommited ...

如果想要在线程dump中包含锁信息,需要加上-l=true:

➜ ~ jcmd 3426 Thread.print -l=true3426:2018-08-02 21:39:08Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode):"Attach Listener" #16 daemon prio=9 os_prio=31 tid=0x00007fd9a43d0000 nid=0xd07 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None"NettythreadDeathWatcher-2-1" #15 daemon prio=1 os_prio=31 tid=0x00007fd9a4ca1800 nid=0xa903 waiting on condition [0x0000700005924000] 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:144) at java.lang.Thread.run(Thread.java:748) Locked ownable synchronizers: - None.... other threads ommited

抓取堆内存

➜ ~ jcmd 3426 help GC.heap_dump3426:GC.heap_dumpGenerate a HPROF format dump of the Java heap.Impact: High: Depends on Java heap size and content. Request a full GC unless the "-all" option is specified.Permission: java.lang.management.ManagementPermission(monitor)Syntax : GC.heap_dump [options] <filename>Arguments: filename : Name of the dump file (STRING no default value)Options: (options must be specified using the <key> or <key>=<value> syntax) -all : [optional] Dump all objects including unreachable objects (BOOLEAN false)

如果你定位过内存泄漏相关的问题,那么你肯定知道如何dump堆内存,或者查看堆内存中的类分布。通常之前我们使用的工具是jmap,使用jcmd转存堆内存的方法是使用GC.heap_dump

➜ ~ jcmd 3426 GC.heap_dump test.dump3426:Heap dump file created

通常情况,在dump堆内存钱会触发一次full GC,如果不希望触发full GC,可以指定参数all=true,即:

➜ ~ jcmd 3426 GC.heap_dump test2.dump -all=true3426:Heap dump file created

统计堆内存使用情况

转存堆内存并分析相对来说比较复杂,一个更简单的方法是统计堆中对象的直方图。在没有jcmd的时候,我通常是使用jmap来统计堆中对象的分布,如果想使用jcmd统计堆中对象的分布,使用jcmd <PID> GC.class_histogram


jcmd命令中包还有其他很多工具,大家如果感兴趣可以通过jcmd <PID> help <command>查看。在没有jcmd工具之前,我们通常需要组合使用多个不同的工具进行问题定位,而现在只需要使用jcmd一个命令就可以了。除了替代现有的工具之后,jcmd还支持更高级的profiling功能,录制jfr来分析JVM的性能。

[文章同步发布在我的个人博客上,欢迎拍砖。传送门: JVM问题定位的瑞士军刀——JCMD

文章评论

Top