OutOfMemoryError on overprovisioned heap

Why am I getting the OutOfMemoryError when allocating a data structure that should happily fit within the heap I have provided for the JVM? This was a question I recently faced.

Indeed, when looking at what the developer was trying to accomplish and triple-checking the heap size given to the JVM via the -Xmx parameter, it indeed seemed that something shady was going on.

30 minutes later we understood the situation and solved the mystery.  But it was indeed not obvious at the first place, so I thought it might save someone a day if I described the underlying problem in more details.

As always, the best way to understand a problem is via a hands-on example. I have constructed a small synthetic test case:

package eu.plumbr.demo;
class ArraySize {
	public static void main(String... args) {
		int[] array = new int[1024*1024*1024];
	}
}

The code is simple – all it tries to do is to allocate an array with one billion elements.  Now, considering that java int primitives require 4 bytes, one might think that running the code with 6g heap would run just fine. After all, those billion integers should consume only 4g memory. So why do I see the following when I execute the code?

My Precious:bin me$ java –Xms6g –Xmx6g eu.plumbr.demo.ArraySize
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
 	at eu.plumbr.demo.ArraySize.main(ArraySize.java:6)

Before just tossing in even more heap (as a matter of fact, with –Xmx7g the example above runs just fine), let us try to understand why our expectation was wrong.

First – the int primitives in java do indeed require 4 bytes.  So it is not like our JVM implementation has gone crazy over night.  And I can assure you that the math is also correct – 1024*1024*1024 int primitives indeed would require 4,294,967,296 bytes or 4 gigabytes.

To understand what is happening, lets run the very same case and turn on garbage collection logging by specifying –XX:+PrintGCDetails:

My Precious:bin me$ java –Xms6g -Xmx6g -XX:+PrintGCDetails eu.plumbr.demo.ArraySize

-- cut for brevity --

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at eu.plumbr.demo.ArraySize.main(ArraySize.java:6)

Heap
 PSYoungGen      total 1835008K, used 125829K [0x0000000780000000, 0x0000000800000000, 0x0000000800000000)
  eden space 1572864K, 8% used [0x0000000780000000,0x0000000787ae15a8,0x00000007e0000000)
  from space 262144K, 0% used [0x00000007e0000000,0x00000007e0000000,0x00000007f0000000)
  to   space 262144K, 0% used [0x00000007f0000000,0x00000007f0000000,0x0000000800000000)
 ParOldGen       total 4194304K, used 229K [0x0000000680000000, 0x0000000780000000, 0x0000000780000000)
  object space 4194304K, 0% used [0x0000000680000000,0x0000000680039608,0x0000000780000000)
 PSPermGen       total 21504K, used 2589K [0x000000067ae00000, 0x000000067c300000, 0x0000000680000000)
  object space 21504K, 12% used [0x000000067ae00000,0x000000067b087668,0x000000067c300000)

The answers are now staring right into our eyes: even though we have plenty of total heap available, no individual area in the heap is large enough to hold 4g of objects. Our 6g heap is divided into four separate regions, sized like this:

  • Eden 1,536M
  • Survivor spaces (from and to) 256M each
  • OldGen 4,096M

Now, bearing in mind that object allocations must fit into a single region we indeed can see that the application stands no chance – there is just not enough room in any of our heap regions to accommodate this single 4g allocation.

So – is our only hope now to increase heap further? Even if we already have over-provisioned by nearly 50% – handing 6g of heap to a data structure which should fit into 4g? Not so fast – there is an alternative solution available. You can set the size of the different areas in memory. It is not as straightforward and user-friendly as one might expect, but two small modifications of the startup configuration will do the trick. When launching the same code with just two extra options:

My Precious:bin me$ java -Xms6g -Xmx6g -XX:NewSize=5g -XX:SurvivorRatio=10 eu.plumbr.demo.ArraySize

then the program does its job and no OutOfMemoryError is being thrown. Adding -XX:+PrintGCDetails to the startup also explains it:

Heap
 PSYoungGen      total 4806144K, used 4369080K [0x00000006c0000000, 0x0000000800000000, 0x0000000800000000)
  eden space 4369408K, 99% used [0x00000006c0000000,0x00000007caaae228,0x00000007cab00000)
  from space 436736K, 0% used [0x00000007e5580000,0x00000007e5580000,0x0000000800000000)
  to   space 436736K, 0% used [0x00000007cab00000,0x00000007cab00000,0x00000007e5580000)
 ParOldGen       total 1048576K, used 0K [0x0000000680000000, 0x00000006c0000000, 0x00000006c0000000)
  object space 1048576K, 0% used [0x0000000680000000,0x0000000680000000,0x00000006c0000000)
 PSPermGen       total 21504K, used 2563K [0x000000067ae00000, 0x000000067c300000, 0x0000000680000000)
  object space 21504K, 11% used [0x000000067ae00000,0x000000067b080c90,0x000000067c300000)

We see that the sizes of regions are now indeed what we asked for:

  • Young size int total (eden + two survivor spaces) is 5g, as specified by our -XX:NewSize=5g parameter
  • Eden is 10x larger than survivor, as we specified with the -XX:SurvivorRatio=10 parameter.

Note that in our case, both of the parameters were necessary. Specifying just the  -XX:NewSize=5g would still split it between eden and survivors in a way where no individual area can hold the required 4g.

Hopefully reading this explanation will save you a day of debugging in the future. Or help you avoid over-provisioning the resources.

 

 

Reference: OutOfMemoryError on overprovisioned heap from our JCG partner Vladimir Sor at the Plumbr Blog blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

One Response to "OutOfMemoryError on overprovisioned heap"

  1. Leonid says:

    What happens when the JVM tries to move this array to the old gen?

Leave a Reply


four + 5 =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close