Short answer
Watch the trend: after each Full GC the used old-gen baseline keeps climbing instead of returning to a steady level — that sawtooth-with-rising-floor pattern signals a leak.
Explanation
Enable GC logging and monitor heap-usage-after-GC via JMX/Micrometer/Prometheus. Capture a heap dump (jmap or -XX:+HeapDumpOnOutOfMemoryError) and analyze dominator trees in Eclipse MAT to find which objects retain the most memory and their GC-root reference chains. Classic culprits: unbounded caches/collections, ThreadLocals never removed, and unclosed resources or listeners.
Common mistakes
- Increasing heap without analysing.
- Not using heap dumps and comparing over time.