The jcmd command – the small command line utility that does a lot
Since Java 8, the JDK has been coming with the jcmd command, which allows you to execute commands on a local Java instance using the same user/group as the instance you want to check.
The usage of jcmd, although command-based, is quite simple. To understand it, we will first start our quote manager application with the command we saw in Chapter 1, Money – The Quote Manager Application:
mvn clean package embedded-glassfish:run
Now in another console, just execute jcmd. On my system, it will dump what follows:
$ jcmd
4981 com.intellij.idea.Main
7704 sun.tools.jcmd.JCmd
7577 org.codehaus.plexus.classworlds.launcher.Launcher clean package embedded-glassfish:run
5180 org.jetbrains.idea.maven.server.RemoteMavenServer
The first column is the process ID (PID) of the program and what follows is the launching command (main and parameters). Since we launched our server with maven, we can identify it with the maven main (org.codehaus.plexus.classworlds.launcher.Launcher) or with the parameters that exactly match the command we launched (clean package embedded-glassfish:run).
If you launch a standalone GlassFish, you will probably have a line like the following:
7877 com.sun.enterprise.glassfish.bootstrap.ASMain -upgrade false -domaindir /home/dev/glassfish5/glassfish/domains/domain1 -read-stdin true -asadmin-args --host,,,localhost,,,--port,,,4848,,,--secure=false,,,--terse=false,,,--echo=false,,,--interactive=true,,,start-domain,,,--verbose=false,,,--watchdog=false,,,--debug=false,,,--domaindir,,,/home/dev/glassfish5/glassfish/domains,,,domain1 -domainname domain1 -instancename server -type DAS -verbose false -asadmin-classpath /home/dev/glassfish5/glassfish/lib/client/appserver-cli.jar -debug false -asadmin-classname com.sun.enterprise.admin.cli.AdminMain
This one is pretty verbose but you can identify that the main (first string) references glassfish and you can find the domains directory to distinguish between multiple instances.
To just give you another idea, if you use Apache Tomcat or TomEE, you will identify it with this line:
8112 org.apache.catalina.startup.Bootstrap start
Now, we have the PID of our Java process; we can pass it to jcmd:
jcmd <PID> help
For example, for our previous maven GlassFish instance, it will look like the following:
jcmd 7577 help
The output should look like the following:
7577:
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
As you can see, the output is basically a list of commands that you can invoke using jcmd. A lot of these commands are informative, such as VM.version (which will just log which JVM you are using), but some commands are actual actions, such as GC.run (which will call System.gc()). Concerning the performance, we are interested in Thread.print, which is a replacement of jstack. GC data commands, such as GC.class_histogram, are related to the garbage collection data, while the JFR commands are related to Java Flight Recorder.
Let's start with the most basic but also probably the most important command: Thread.print. This will allow us to see what our application is doing by digging into the current thread stack of our application.