jvm 性能调优工具之 jcmd

实践

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

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

[root@vincent-testing ~]# jcmd -h
Usage: 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 -l
13714 sun.tools.jcmd.JCmd -l
16324 org.rzo.yajsw.app.WrapperJVMMain
9350 org.rzo.yajsw.app.WrapperJVMMain
9815 org.apache.catalina.startup.Bootstrap start

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

操作指定的JVM进程

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

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

For more information about a specific command use 'help <command>'.

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

抓现场堆栈

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

➜  ~ jcmd 3426 Thread.print
3426:
2018-08-02 21:36:07
Full 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=true
3426:
2018-08-02 21:39:08
Full 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_dump
3426:
GC.heap_dump
Generate 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.dump
3426:
Heap dump file created

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

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

统计堆内存使用情况

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


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

 

各参数含义

概述

在JDK1.7以后,新增了一个命令行工具 jcmd。他是一个多功能的工具,可以用它来导出堆、查看Java进程、导出线程信息、执行GC、还可以进行采样分析(jmc 工具的飞行记录器)。

命令格式

  jcmd <pid | main class> <command ... | PerfCounter.print | -f  file>
  jcmd -l
  jcmd -h

描述

  • 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 -l

命令:jcmd -l
描述:查看 当前机器上所有的 jvm 进程信息

  jcmd 
  jcmd -l
  jps 

这三个命令的效果是一样的

查看性能统计

命令:jcmd pid PerfCounter.print
描述:查看指定进程的性能统计信息。

C:\Windows\system32>jcmd 9592 PerfCounter.print
9592:
java.ci.totalTime=16704
java.cls.loadedClasses=438
java.cls.sharedLoadedClasses=0
java.cls.sharedUnloadedClasses=0
java.cls.unloadedClasses=0
java.property.java.class.path="D:\work\git\test\target\classes"
java.property.java.endorsed.dirs="D:\Program Files\Java\jre1.8.0_91\lib\endorsed"
java.property.java.ext.dirs="D:\Program Files\Java\jre1.8.0_91\lib\ext;C:\Windows\Sun\Java\lib\ext"
java.property.java.home="D:\Program Files\Java\jre1.8.0_91"
...

列出当前运行的 java 进程可以执行的操作

命令:jcmd PID help

C:\Windows\system32>jcmd 9592 help
9592:
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 11772 help JFR.dump

1.JRF 相关命令

JRF 功能跟 jmc.exe 工具的飞行记录器的功能一样的。
要使用 JRF 相关的功能,必须使用 VM.unlock_commercial_features 参数取消锁定商业功能 。

jmc.exe 显示的提示

  • 启动JFR
    执行命令:jcmd $PID JFR.start name=abc,duration=120s

  • Dump JFR
    等待至少duration(本文设定120s)后,执行命令:jcmd PID JFR.dump name=abc,duration=120s filename=abc.jfr(注意,文件名必须为.jfr后缀)

  • 检查JFR状态
    执行命令:jcmd $PID JFR.check name=abc,duration=120s

  • 停止JFR
    执行命令:jcmd $PID JFR.stop name=abc,duration=120s

  • JMC分析
    切回开发机器,下载步骤3中生成的abc.jfr,打开jmc,导入abc.jfr即可进行可视化分析

VM.uptime

命令:jcmd PID VM.uptime
描述:查看 JVM 的启动时长:

GC.class_histogram

命令:jcmd PID GC.class_histogram
描述:查看系统中类统计信息

这里和jmap -histo pid的效果是一样的
这个可以查看每个类的实例数量和占用空间大小。

Thread.print

命令:jcmd PID Thread.print
描述:查看线程堆栈信息。

该命令同 jstack 命令。

GC.heap_dump

命令:jcmd PID GC.heap_dump FILE_NAME
描述:查看 JVM 的Heap Dump

C:\Users\jjs>jcmd 10576 GC.heap_dump d:\dump.hprof
10576:
Heap dump file created

跟 jmap命令:jmap -dump:format=b,file=heapdump.phrof pid 效果一样。
导出的 dump 文件,可以使用MAT 或者 Visual VM 等工具进行分析。

注意:如果只指定文件名,默认会生成在启动 JVM 的目录里。

VM.system_properties

命令:jcmd PID VM.system_properties
描述:查看 JVM 的属性信息

C:\Users\jjs>jcmd 10576 VM.system_properties
10576:
#Wed Jan 31 22:30:20 CST 2018
java.vendor=Oracle Corporation
osgi.bundles.defaultStartLevel=4
......
os.version=10.0
osgi.arch=x86_64
path.separator=;
java.vm.version=25.91-b15
org.osgi.supports.framework.fragment=true
user.variant=
osgi.framework.shape=jar
java.awt.printerjob=sun.awt.windows.WPrinterJob
osgi.instance.area.default=file\:/C\:/Users/jjs/eclipse-workspace/
sun.io.unicode.encoding=UnicodeLittle
org.osgi.framework.version=1.8.0
......

VM.flags

命令:jcmd PID VM.flags
描述:查看 JVM 的启动参数

C:\Users\jjs>jcmd 10576 VM.flags
10576:
-XX:CICompilerCount=3 -XX:ConcGCThreads=1 
-XX:G1HeapRegionSize=1048576 -XX:InitialHeapSize=268435456 
-XX:MarkStackSize=4194304 -XX:MaxHeapSize=1073741824 
-XX:MaxNewSize=643825664 -XX:MinHeapDeltaBytes=1048576 
-XX:+UseCompressedClassPointers -XX:+UseCompressedOops 
-XX:+UseFastUnorderedTimeStamps -XX:+UseG1GC 
-XX:-UseLargePagesIndividualAllocation -XX:+UseStringDeduplication

VM.command_line

命令:jcmd PID VM.command_line
描述:查看 JVM 的启动命令行

C:\Users\jjs>jcmd 10576 VM.command_line
10576:
VM Arguments:
jvm_args: -Dosgi.requiredJavaVersion=1.8 
-Dosgi.instance.area.default=@user.home/eclipse-workspace 
-XX:+UseG1GC -XX:+UseStringDeduplication 
-Dosgi.requiredJavaVersion=1.8 -Xms256m -Xmx1024m
java_command: <unknown>
java_class_path (initial): D:\tool\...\org.eclipse.equinox.launcher.jar

GC.run_finalization

命令:jcmd PID GC.run_finalization
描述: 对 JVM 执行 java.lang.System.runFinalization()

C:\Users\jjs>jcmd 10576 GC.run_finalization
10576:
Command executed successfully

执行一次finalization操作,相当于执行java.lang.System.runFinalization()

GC.run

命令:jcmd PID GC.run
描述:对 JVM 执行 java.lang.System.gc()

C:\Users\jjs>jcmd 10576 GC.run
10576:
Command executed successfully

告诉垃圾收集器打算进行垃圾收集,而垃圾收集器进不进行收集是不确定的。

PerfCounter.print

命令:jcmd PID PerfCounter.print
描述:查看 JVM 性能相关的参数

C:\Users\jjs>jcmd 10576 PerfCounter.print
10576:
java.ci.totalTime=93024843
java.cls.loadedClasses=18042
java.cls.sharedLoadedClasses=0
java.cls.sharedUnloadedClasses=0
java.cls.unloadedClasses=3

......

VM.version

命令:jcmd PID VM.version
描述:查看目标jvm进程的版本信息

C:\Users\jjs>jcmd 10576 VM.version
10576:
Java HotSpot(TM) 64-Bit Server VM version 25.91-b15
JDK 8.0_91

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要查看 Java 程序的内存占用,可以使用以下几种方法之一: 1. 使用 jconsole 工具:jconsole 是 Java 自带的一个图形化监视工具,可以实时查看 Java 程序的内存使用情况。 2. 使用 jstat 命令行工具:jstat 是 Java 自带的一个命令行工具,可以实时查看 Java 程序的内存使用情况。 3. 使用 VisualVM 工具:VisualVM 是一个免费、开源的 Java 监视工具,可以实时查看 Java 程序的内存使用情况。 4. 使用 Java Management Extension (JMX) 接口:Java 程序可以通过 JMX 接口暴露内存使用信息,可以使用第三方工具(如 Hyperic HQ、Nagios、Zabbix 等)监视 Java 程序的内存使用情况。 5. 使用 Java 程序内置的监视功能:Java 程序可以通过内置的监视功能(如 Runtime.getRuntime().totalMemory() 和 Runtime.getRuntime().freeMemory() 等方法)获取内存使用信息。 此外,还可以使用系统工具(如 top、ps、vmstat 等)查看 Java 程序的内存占用情况。 ### 回答2: 要查看Java程序的内存占用,可以使用Java虚拟机监控工具JVM Monitor)或Java VisualVM工具。 使用JVM Monitor时,首先需要在Java程序的启动命令中加入"-Dcom.sun.management.jmxremote"参数,以开启对JMX(Java Management Extensions)的支持。然后,通过JVM Monitor的界面可以实时查看Java程序的内存占用情况,包括堆内存和非堆内存的使用情况,以及垃圾收集等相关信息。 另一种方法是使用Java VisualVM,它是一种功能强大的Java性能分析工具。它可以通过JMX或本地连接方式连接到正在运行的Java程序,并提供详细的性能监控和分析功能。在Java VisualVM中,可以通过"Monitor"选项卡查看Java程序的内存占用情况,包括堆内存使用情况、线程数、垃圾收集情况等。 除了这些工具之外,还可以使用一些命令行工具来查看Java程序的内存占用情况,如jcmd、jinfo、jmap等。通过这些命令,可以获取Java虚拟机进程的一些参数和统计信息,包括堆内存使用情况、垃圾收集器类型等。 总之,要查看Java程序的内存占用,可以使用JVM Monitor、Java VisualVM工具,或者使用一些命令行工具,这些工具能够提供详细的内存监控和分析信息,帮助我们了解程序的内存使用情况,及时发现和解决内存泄漏等问题,提高程序的性能和稳定性。 ### 回答3: 要查看Java内存占用,可以使用Java虚拟机自带的工具或第三方工具。 对于Java虚拟机自带的工具,可以使用以下命令: 1. jps命令可以显示当前系统中所有Java进程的进程号。通过查看进程号,可以找到指定的Java进程。 2. jstat命令可以显示Java进程的各个内存区域的使用情况,包括堆内存、非堆内存、新生代、老年代等。 3. jmap命令可以生成Java进程的内存快照,并显示内存中对象的统计信息。可以通过该命令查看Java进程的内存占用情况。 4. jhat命令用于分析jmap生成的内存快照文件,可以在浏览器中以可视化的方式查看内存占用情况。 此外,还有一些第三方工具可以帮助查看Java内存占用,比如VisualVM、JConsole等,它们提供了更丰富的界面和功能来分析和监控Java应用程序的内存使用情况。 总结起来,查看Java内存占用可以通过Java虚拟机自带的工具(如jps、jstat、jmap、jhat)或第三方工具(如VisualVM、JConsole)来实现。通过这些工具,我们可以获取Java进程的内存使用情况,帮助分析和优化Java应用程序的性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值