Skip to content

Debugging, Profiling, and Memory Analysis

Debugging explains incorrect behavior. Profiling measures where resources are spent. A debugger can reveal state at one moment; a profiler can reveal hot paths or allocation patterns across time. Neither substitutes for a reproducible contract and evidence.

A disciplined investigation

  1. State expected and observed behavior precisely.
  2. Preserve timestamps, inputs, versions, configuration, and correlation IDs.
  3. Reproduce at the smallest safe scope, or characterize frequency when rare.
  4. Form a falsifiable hypothesis.
  5. Choose one measurement that distinguishes it from alternatives.
  6. Fix the cause, add a regression test, and verify operational signals.

Change one relevant variable at a time. Binary search through versions, inputs, or the execution path often narrows a large search space. Keep facts separate from inference in incident notes.

CPU and wall-clock profiling

Sampling profilers estimate where CPU time or wall time accumulates with lower overhead than recording every call. Interpret profiles using the chosen mode: blocked time, CPU time, and elapsed time answer different questions. A hot method may be a caller's symptom, so inspect call trees and representative load.

For Java, Java Flight Recorder can capture CPU samples, allocation, locks, GC, and other runtime events. Use JMH for microbenchmarks; do not infer throughput from a debugger or one System.nanoTime() measurement.

Memory analysis

Distinguish live heap, allocation rate, native memory, and retained resources. A high allocation rate is not necessarily a leak; a leak is unwanted retained reachability or an unreleased external resource. Compare heap histograms or dumps, inspect dominator and retention paths, and correlate with GC behavior.

Heap dumps and recordings may contain secrets or personal data. Capture, transfer, retain, and delete them under an explicit security policy. Start with lower-overhead production evidence and reproduce elsewhere when possible.

Concurrency evidence

Thread dumps reveal lock ownership, waiting, deadlock, and pool saturation at a moment. Several samples distinguish a persistent stall from normal waiting. Correlate executor queues, connection acquisition, downstream latency, and bounded-resource metrics.

After a fix, keep a focused regression test, compare the original measurement, and remove temporary high-volume logging or unsafe diagnostic endpoints.