{"id":1,"date":"2026-07-01T14:21:12","date_gmt":"2026-07-01T14:21:12","guid":{"rendered":"https:\/\/javaconcurrency.com\/?p=1"},"modified":"2026-07-01T18:23:29","modified_gmt":"2026-07-01T18:23:29","slug":"how-to-automate-analysis-of-huge-jvm-heap-dump-files","status":"publish","type":"post","link":"https:\/\/javaconcurrency.com\/index.php\/2026\/07\/01\/how-to-automate-analysis-of-huge-jvm-heap-dump-files\/","title":{"rendered":"How To Automate Analysis Of Huge Jvm Heap Dump Files"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The trigger: we observed JVM crashes with java.lang.OutOfMemoryError.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The goal: find a tool that can analyze large (&gt;10 GB) heap dump files created on OOM Error, analyze them in automated way and give a binary answer whether a memory leak was detected or not.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There are several tools on the market which claim to be able to analyze the heap dump files: Shark, VisualVM CLI, JProfiler CLI \/ YourKit CLI, IBM Heap Analyzer, JHAT, Eclipse Memory Analyzer Tool. An additional alternative is to write own analysis tool.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The winner is Eclipse Memory Analyzer Tool.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Details<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s take a look at the options we have.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Shark (Android)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This tool is designed specifically for Android and it is not suitable for:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Backend services<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Microservices<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">JVM heap dumps<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Large heaps (GBs)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">VisualVM CLI<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Limited automation<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Small heaps only<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">JProfiler CLI \/ YourKit CLI<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Commercial<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Very strong automation<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Excellent diff engine<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I work with YourKit tool frequently doing manual analysis (like heap utilization or memory leaks). It is very useful and powerful, but so far I have used it on living java processes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This tools cannot analyze .hprof files. It can dump, create and analyze snapshot files. I did not find any tool how to convert .hprof to a snapshot.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So if we want to release automated analysis tool we have to rely on customers having JProfiler \/ YourKit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">IBM Heap Analyzer (HA)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Excellent for OpenJ9 (high-performance Java Virtual Machine (JVM) originally developed by IBM). Since I am using a different JVM, I decided not to test this solution<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CLI automation<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">JHAT<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">It is not supported anymore.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Eclipse Memory Analyzer Tool (MAT)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">MAT can check for specific class causing leak and will give true\/false result for this class.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">MAT can compare two hprof files.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">MAT is the winner of my tool selection. It successfully passed several of my test cases. I have not used it in production environment so far.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Writing own analysis tool.<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is extremely complex and time consuming. Below some aspects of it (I compare it to the MAT).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">JVM Heap Dumps Are Massive Graphs. A .hprof file contains:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Millions of objects<br>Billions of references<br>Deep object hierarchies<br>Mixed primitive arrays, strings, classes, threads, GC roots<br>Example: a 1\u202fGB server heap can have 20\u201350 million objects.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To detect leaks, we must analyze all objects&#8217; reachability. That is literally a giant directed graph.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Challenge: We need a memory-efficient graph representation \u2014 otherwise our analyzer will run out of memory before it finishes analyzing the heap.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Heap dump format is tricky as .hprof files are binary files. There are:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Multiple formats depending on VM<br>Object ID sizes differ per JVM (32-bit vs 64-bit)<br>Classes, fields, arrays, primitives are encoded differently<br>References vs primitive values require different parsing<br>So we need a robust, low-level parser. One small mistake = parser crashes on a 20\u202fGB heap.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We need GC root resolution<br>Leaks are only defined relative to GC roots (things that are always reachable).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Static fields<br>Threads<br>JNI references<br>Classloaders<br>We must implement:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A system that finds all roots<br>Correctly identifies strong vs weak references<br>Tracks reachable vs unreachable objects<br>Otherwise, we will misreport leaks.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dominator tree computation is non-trivial<br>To find retained memory (what a leak \u201cholds\u201d):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We must build a dominator tree of the heap graph. This involves graph algorithms on millions of nodes. Naive implementations can use hundreds of GB of RAM for large heaps. Eclipse MAT does this efficiently using memory-mapped indexes, compression, and optimized algorithms.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Rewriting it yourself = months of careful work.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We need retained size calculations<br>To detect leaks:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We must sum memory recursively from dominator tree. Count arrays, object headers, references correctly. Handle shared subgraphs properly<br>Mistakes here \u2192 false positives\/negatives.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We need scalability + stability<br>Our tool must handle:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1\u202fGB \u2192 small test dumps (easy)<br>40\u2013100\u202fGB production dumps (hard)<br>Running in headless, CI\/CD environments<br>Parsing without crashing<br>Most homegrown parsers fail silently on production dumps.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We need automation \/ reporting \/ error handling. Even if we parse the heap correctly:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We must generate reports programmatically<br>Identify \u201cProblem Suspects\u201d<br>Handle malformed dumps<br>Support multiple JVM types<br>All this adds more complexity.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Industry estimate<br>Rewriting MAT from scratch is approximately:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>Step<\/td><td>Time estimate<\/td><\/tr><tr><td>HPROF parser<\/td><td>2\u20133 months (some hprof parsers can be found on GitHub)<\/td><\/tr><tr><td>Graph builder + dominator tree<\/td><td>2\u20133 months<\/td><\/tr><tr><td>Retained size calculation<\/td><td>1 month<\/td><\/tr><tr><td>Leak detection heuristics<\/td><td>1 month<\/td><\/tr><tr><td>Testing &amp; validation<\/td><td>2\u20133 months<\/td><\/tr><tr><td>Total<\/td><td>~8\u201312 months<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And that\u2019s for a team of engineers experienced in JVM internals.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Summary<br>Reasons why it is \u201cextremely complex\u201d:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">JVM heaps are massive and intricate graphs<br>.hprof binary format is complicated and JVM-dependent<br>Must correctly identify GC roots<br>Must compute dominator trees efficiently<br>Must calculate retained memory precisely<br>Must scale to production heaps<br>Must generate usable, reliable reports<br>Bottom line: We would be basically rewriting Eclipse MAT, which has been refined over 15+ years.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of writing our own parser:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We can use MAT Core + CLI automation<br>Focus on automating leak detection logic<br>We get all graph algorithms, retained size, dominators, and reports for free<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Our work becomes hours of automation and Java code, not months of low-level engineering.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Notice: despite <strong>years of development<\/strong>, MAT is still not 100% correct:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Off-Heap Blindness: It cannot track native memory leaks (like DirectByteBuffers or JNI allocations) because they don&#8217;t live in the .hprof file.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Unreachable Objects: Automated dumps can capture &#8220;dead&#8221; objects not yet swept by GC, sometimes leading to false positives depending on the garbage collector algorithm used (e.g., ZGC or Shenandoah).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Complex Cyclic Dependencies: If objects reference each other in massive circular chains, MAT&#8217;s Dominator Tree algorithm can struggle to isolate a single root suspect.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\ude80 What&#8217;s Next?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you are interested to see code example, where I build a fully headless Java orchestrator using ProcessBuilder to parse .hprof file automatically please read: <a href=\"https:\/\/javaconcurrency.com\/index.php\/2026\/07\/01\/automating-jvm-heap-dump-analysis-a-complete-java-implementation\/\" data-type=\"post\" data-id=\"43\">Automating JVM Heap Dump Analysis \u2013 A Complete Java Implementation<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The trigger: we observed JVM crashes with java.lang.OutOfMemoryError. The goal: find a tool that can analyze large (&gt;10 GB) heap dump files created on OOM Error, analyze them in automated way and give a binary answer whether a memory leak was detected or not. There are several tools on the market which claim to be [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-1","post","type-post","status-publish","format-standard","hentry","category-jvm-memory-management"],"_links":{"self":[{"href":"https:\/\/javaconcurrency.com\/index.php\/wp-json\/wp\/v2\/posts\/1","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/javaconcurrency.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/javaconcurrency.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/javaconcurrency.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/javaconcurrency.com\/index.php\/wp-json\/wp\/v2\/comments?post=1"}],"version-history":[{"count":13,"href":"https:\/\/javaconcurrency.com\/index.php\/wp-json\/wp\/v2\/posts\/1\/revisions"}],"predecessor-version":[{"id":53,"href":"https:\/\/javaconcurrency.com\/index.php\/wp-json\/wp\/v2\/posts\/1\/revisions\/53"}],"wp:attachment":[{"href":"https:\/\/javaconcurrency.com\/index.php\/wp-json\/wp\/v2\/media?parent=1"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/javaconcurrency.com\/index.php\/wp-json\/wp\/v2\/categories?post=1"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/javaconcurrency.com\/index.php\/wp-json\/wp\/v2\/tags?post=1"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}