Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
173: Heap Dump Analysis Basics
How do I actually get a heap dump from a running application?
In a perfect world, you'd just click a button. In reality, you're usually dealing with a production server that's crawling because it's almost out of memory. You have two main options here. First, there's the "preventative" approach: adding -XX:+HeapDumpOnOutOfMemoryError to your JVM arguments. This tells Java, "If you're about to crash, please leave a crime scene report (the .hprof file) behind so I can figure out what happened."
But what if the app hasn't crashed yet, but it's acting sluggish? I usually reach for jmap. It's a command-line tool that comes with the JDK. You'll need the PID (Process ID) of your app, and then you run something like this:
jmap -dump:format=b,file=heapdump.hprof 12345
One huge warning: taking a heap dump triggers a "Stop-the-World" event. The JVM literally freezes while it writes the memory state to disk. If you have a 32GB heap, your application is going to be unresponsive for several seconds (or longer). Never do this on a high-traffic production node without alerting your team first, or you'll have a very confused SRE wondering why the health checks are failing.
Once I open the file, what am I actually looking for?
If you're using Eclipse MAT (Memory Analyzer Tool)—which is the industry standard for a reason—you'll see a lot of noise. The secret is to ignore the total number of objects and focus on the Retained Heap.
Shallow heap is just the size of the object itself. Retained heap is the size of the object plus everything it's holding onto that would be deleted if the object were garbage collected. I've seen countless devs get distracted by millions of String objects. Strings are almost always the symptom, not the cause. You want to find the "big" objects—the ones holding the references to those strings.
Look for the "Leak Suspects" report in MAT. It'll often point you to something like a ConcurrentHashMap or a ArrayList that's grown to 500MB. That's your smoking gun.
How do I prove it's a leak and not just a large cache?
This is where most people get stuck. Just because a map is huge doesn't mean it's a leak; it might just be a cache that's working as intended. To prove it's a leak, you need to find the Path to GC Roots.
In MAT, you right-click the suspect object and select "Path to GC Roots" > "exclude all phantom/weak/soft references." This shows you exactly who is holding a "strong" reference to that memory. If you see a chain that leads back to a static variable in a SessionManager class, you've found your leak.
For example, I once debugged a system where a HashMap was growing forever. The path to GC root revealed that the keys were custom UserKey objects, but the developer forgot to override equals() and hashCode(). Because of that, the map kept adding "duplicate" keys that it thought were unique, and they never got evicted. The heap dump made that obvious because I could see 10,000 instances of the same user ID in the map.
📋 Practical Task
Debugging the SessionLeak Service
You are tasked with fixing a memory leak in a legacy SessionTracker class. The application is crashing every 48 hours. You have analyzed the heap dump and found that a HashMap named activeSessions is consuming 90% of the retained heap, and the Path to GC Roots shows it's held by a static reference in the SessionTracker class.
Below is the problematic code. Identify the cause of the leak and rewrite the removeSession method or the map implementation to ensure memory is reclaimed when sessions expire or are closed.
public class SessionTracker {
// The heap dump shows this map grows indefinitely
private static final Map<String, UserSession> activeSessions = new HashMap<>();
public static void createSession(String sessionId, UserSession session) {
activeSessions.put(sessionId, session);
}
public static void removeSession(String sessionId) {
// TODO: Fix the leak.
// Currently, the developer is only logging the removal
// but not actually removing the object from the map.
System.out.println("Closing session: " + sessionId);
}
}
Requirements:
- Fix the
removeSessionmethod to actually clear the reference. - Suggest a change to the
Mapimplementation (e.g., usingWeakHashMapor a proper cache library) and explain why that would prevent a total crash even ifremoveSessionisn't called.
There are no comments for now.