Running out of memory without the OutOfMemoryError
This is actually a reincarnation of a post originally posted in ~2010. The flashback occurred when listening to our engineers cursing at a particularly nasty bug raising its head yesterday. When the cursing stopped, I stepped by to verify my doubts. Lo and behold, I was correct – the mood swing was caused by an app running out of the heap space but dying without the usual OutOfMemoryError symptom.
So let me walk through the case of missing OutOfMemoryError with the same code example I first encountered three years ago. Back then I was using a Windows XP with a mid-2010 JDK 6 installed.
I was playing around with an early release of Plumbr which was supposed to find memory leaks from the application (edit: back then it failed to do pretty much anything besides crashing the JDK). In order to verify this, I wrote a small snippet which I thought would be a good test case for a leak discovery (edit: in reality it is not). I was able to create and launch the following:
class Leak { static List list = new ArrayList(); public static void main(String[] args) { for (int i = 0; i >= 0 ;i++) { list.add(i); } System.out.println("I will either reach here or die trying"); } }
Pretty good for a marketroid, eh? But what do you think running the code displayed to my command prompt:
Option A:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:2760) at java.util.Arrays.copyOf(Arrays.java:2734) at java.util.ArrayList.ensureCapacity(ArrayList.java:167) at java.util.ArrayList.add(ArrayList.java:351) at Leak.main(Leak.java:6)
Option B:
I will either reach here or die trying
Well, as I found out, it doesn’t print out anything, so I was pretty much just stuck to staring the empty command prompt.
As this was two years from my last real Java development experience before being demoted to management, I was out of ammo to troubleshoot the situation. So I took the sample to the hardcore Java hackers who later became known as the founders of Zeroturnaround. For 10 good minutes I managed to have bedazzled look on them as well, before it struck – the memory will be allocated in such a way that there is no room for new OutOfMemoryError()
to be created.
If you execute the above with 64MB heap (the default) on Windows XP using a mid 2010 JDK builds, you see a silent failure:
C:\work\snippets\leak java -Xmx64m Leak C:\work\snippets\leak
But if you increase (well actually modify) the heap size a bit, you will encounter a more familiar situation:
C:\work\snippets\leak java -Xmx65m Leak Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:2760) at java.util.Arrays.copyOf(Arrays.java:2734) at java.util.ArrayList.ensureCapacity(ArrayList.java:167) at java.util.ArrayList.add(ArrayList.java:351) at Leak.main(Leak.java:6) C:\work\snippets\leak
Moral of the story? I can only recommend to upgrade to a more modern release – no matter which configuration I tried, I failed to recreate the situation using the JDK 7 builds I had on my Mac today. But looking at the statistics about popular Java runtime configurations, you see a staggering amount of JDK deployments dating back to pre-2010 releases, meaning that the problem is still out there forcing developers to pull crazy all-nighters trying to figure out the source of the problem without any hints from the stacktrace to support them.
Anyhow, backing the engineering team with my extensive knowledge about JDK 6 internals I went ahead and revived the post from the long-forgotten blog I used to contribute back then.