How to Debug Java Applications Like a Pro

Why Debugging Isn’t Just Fixing Bugs

Bob’s Spring Boot app is acting up:

  • It’s slow under load
  • It hangs randomly
  • CPU is spiking without obvious cause
  • Memory usage grows over time

Traditional logging won’t cut it. Bob needs to see what the JVM sees.

Let’s level up from “println debugging” to JVM introspection.

Tools of the Trade

1. Thread Debugging with jstack

Use when:

  • Your app hangs
  • CPU is high
  • You suspect deadlocks or thread leaks

Example:

jstack > thread-dump.txt

Then open with tools like:

  • FastThread.io
  • IntelliJ / VisualVM
  • Look for BlockedWaiting, or RUNNABLE with high CPU

2. Heap Analysis with jmap & VisualVM

Use when:

  • OutOfMemoryError
  • Memory leaks
  • GC isn’t reclaiming memory

Dump heap:

jmap -dump:live,format=b,file=heap.hprof

Open in:

  • VisualVM (free)
  • Eclipse MAT (Memory Analyzer Tool)
  • YourKit (commercial)

Look for:

  • Dominator trees
  • Retained objects
  • Unclosed resources

3. CPU Profiling with Java Flight Recorder (JFR)

Use when:

  • CPU is spiking
  • Methods are slow
  • You need real-time JVM profiling

Record a flight:

jcmd JFR.start name=debug settings=profile filename=recording.jfr

Let it run for a while, then:

jcmd JFR.stop name=debug

Open .jfr file in:

  • JDKMission Control (JMC)
  • View: hot methods, GC, memory, threads

Very low overhead — safe in production!

4. Async Profiler + Flamegraphs

Use when:

  • You want visual flamegraphs
  • You’re debugging CPU usage or method hotspots

Run with:

./profiler.sh -d 30 -f cpu.svg

Opens an interactive flamegraph:

  • Wide = time spent
  • Tall = stack depth

Perfect for understanding hot code paths and performance bottlenecks.

5. Live Debugging with VisualVM

Best for:

  • Quick CPU/memory snapshots
  • Watching GC in real time
  • Sampling threads

Install VisualVM and attach to your running app:

  • View heap usage
  • Track thread states
  • Take heap/thread dumps

Free, built into most JDKs
Great for dev/staging environments

6. Live Instrumentation with BTrace

Want to trace method arguments and return values in a running app?

Example trace:

@OnMethod( clazz=”com.myapp.Service”, method=”process”, location=@Location(Kind.ENTRY) ) public static void onEnter(@Self Object self, String arg) { println(“Entering with arg: ” + arg); }

Zero restart
Live instrumentation
Perfect for debugging unknown behavior without changing code

Bob’s Debugging Flow

Bob doesn’t guess. He observesrecords, and targets the problem like a real JVM detective.

Pro Tips

  • Combine JFR + async-profiler for full insight
  • Always run with -XX:+HeapDumpOnOutOfMemoryError in prod
  • Use sampling instead of tracing in production
  • Avoid -agentlib:hprof (deprecated & heavy)

Debugging like a pro means:

  • Observing the JVM itself
  • Using the right tool for the job
  • Visualizing CPU, memory, and threads — not guessing

Bob can now:

  • Catch memory leaks
  • Profile hotspots
  • Reduce latency
  • Untangle thread issues

Find us

linkedin Shant Khayalian
Facebook Balian’s
X-platform Balian’s
web Balian’s
Youtube Balian’s

#javadebugging #springboot #jvmprofiling #memoryleak #javaflamegraph #visualvm #jfr #asyncprofiler #threadanalysis #performancetuning

Leave a Reply

Your email address will not be published. Required fields are marked *