The JDK ships with command-line diagnostic tools for inspecting running JVM processes — essential for troubleshooting production issues.

jps — List Java Processes

  jps                    # list running Java processes
jps -l                 # show full main class / JAR name
jps -v                 # show JVM arguments
jps -m                 # show main method arguments
  

Output:

  12345 MyApplication
67890 org.apache.catalina.startup.Bootstrap
  

jcmd — Swiss Army Knife

The primary diagnostic tool (Java 7+). Send commands to a running JVM:

  jcmd                          # list all Java processes
jcmd 12345 help               # available commands for this process
jcmd 12345 VM.flags           # show JVM flags
jcmd 12345 VM.system_properties  # system properties
jcmd 12345 VM.command_line    # full command line
jcmd 12345 GC.heap_info       # heap configuration
jcmd 12345 GC.class_histogram # object count by class
jcmd 12345 Thread.print       # thread dump (same as jstack)
jcmd 12345 JFR.start duration=60s filename=recording.jfr
  

Generate Heap Dump

  jcmd 12345 GC.heap_dump /tmp/heap.hprof
  

Run GC

  jcmd 12345 GC.run
  

jmap — Memory Map

  # Heap summary
jmap -heap 12345

# Histogram of objects on heap
jmap -histo 12345
jmap -histo:live 12345    # after full GC — only live objects

# Generate heap dump
jmap -dump:live,format=b,file=/tmp/heap.hprof 12345
  

Sample histogram output:

   num     #instances         #bytes  classname
-------------------------------------------------------
   1:        500000       12000000  java.lang.String
   2:        100000        8000000  java.util.HashMap$Node
   3:         50000        4000000  com.example.User
  

jstack — Thread Dump

Captures the state of all threads — essential for diagnosing deadlocks and performance issues:

  jstack 12345 > /tmp/threaddump.txt
jstack -l 12345    # with lock info
  

Sample output:

  "main" #1 prio=5 os_prio=0 tid=0x00007f8a1c00a000 nid=0x1234 runnable
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at com.example.Client.readResponse(Client.java:45)

"pool-1-thread-1" #12 prio=5 os_prio=0 tid=0x00007f8a1c012800 nid=0x5678 waiting on condition
   java.lang.Thread.State: WAITING (parking)
        at jdk.internal.misc.Unsafe.park(Native Method)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:211)
  

Detecting Deadlocks

  jstack 12345 | grep -A 20 "Found one Java-level deadlock"
  

jstack automatically reports deadlocks at the end of the dump.

Typical Troubleshooting Workflow

  1. jps -l                    → find process ID
2. jcmd <pid> VM.flags        → check JVM configuration
3. jcmd <pid> GC.heap_info    → check heap usage
4. jmap -histo:live <pid>     → find memory hogs
5. jstack <pid>               → check for deadlocks / blocked threads
6. jcmd <pid> GC.heap_dump    → capture heap for analysis (if OOM)
  

Analyzing Heap Dumps

Open .hprof files with:

  • Eclipse MAT (Memory Analyzer Tool) — recommended
  • VisualVM
  • IntelliJ IDEA — built-in profiler

Look for:

  • Large collections (HashMap, ArrayList) retaining too many objects
  • Class loader leaks
  • Unclosed connections or streams

Best Practices

  • Capture thread dumps before restarting a hung application
  • Take heap dumps during low-traffic periods — dumps pause the JVM
  • Use jcmd as the primary tool — it is safer than jmap on production (less pause time)
  • Automate periodic thread dump capture for intermittent issues
  • Always note the PID and timestamp when saving diagnostic output