TL;DR: After upgrading our stack, our Java service's memory usage kept climbing steadily despite flat request volumes. It looked like a single memory leak on our APM dashboard, but digging deeper revealed three separate issues working together : virtual thread payload retention, an unbounded ANTLR 4 static DFA cache in Hibernate 7, and elevated baseline class loading. Here is how we diagnosed and fixed each one.
🔍 The Symptoms
Right after migrating our core service to JDK 25 and Spring Boot 4.1 (which brings in Hibernate 7.4), we noticed our memory graph taking an upward slope:
Memory (MB) ^ | /---- Spikes (Virtual Threads) | / | /\ /| /---- Steady Growth (ANTLR 4 DFA Cache) | / \ / | / |/____\/__|__/______ Baseline Shift (38.8k Classes Loaded) +-------------------------------------> Time
Enter fullscreen mode
Exit fullscreen mode
Request volume was completely flat. Our high-level metrics suggested a classic memory leak, but a single fix didn't resolve it.…