Only getting 340 Megs of usable heap out of 2 Gigs in Java – why the limit?
The challenge – a colleague is trying to write a memory intensive application, he’s trying to allocate large chunks of memory and nothing seems to work. Here are his notes:
I’m trying do some memory intensive work in Java and am running into limits far sooner than I would expect.
First of I would like to access as much of the 2GB process limit I can. It would appear that 1.4 GB is the largest amount that can be allocated with the -Xmx switch. So be it.However we only appear to get use of a small chunk of that. Maxing out around 340MB. Who takes that space? How can get more of it?
Some examples of the problem: I allocate an array (1- or 3-dimensional) of 50 million ints and another array of 10 million doubles. The raw space is 280MB — 200MB for the ints and 80 MB for the doubles. I see the process virtual memory grow to about 290MB, an acceptable overhead. So far so good. Then I do some simple operations involving instantiations of small numbers of small objects. Almost immediately, and fairly frequently, the garbage collector runs (I use -verbose:gc to see when) and the virtual memory of the process jumps to about 460MB. It seems to allocate a fixed fraction of the space consumed on the heap for use during garbage collection.
As another example, I have an application which accumulates values into large int and double arrays (similar to the above). The greatest fraction by far of memory I’m using is in those arrays; the program is creating, then dropping references to small objects whose primitive contents are used to fill the big arrays. The process crashes out-of-memory — exceeds 1.4GB in VM size — when I’ve built arrays that total about 340MB.
This project is running under Java 1.4 – due to limits imposed by our clients. However it would nice to know if things get better in future versions.
So the question for you my reader is there a way to work around this? Who uses all the extra memory?
If you enjoyed this post, subscribe now to get free updates.
July 6, 2007 at 5:37 pm | bloid
I run a process on a 64bit 4 core RHEL system which needs 11GB of ram (scanning the human and mouse genomes simultaneously) just by using the -Xmx11G flag, and runs fine on Java 5 and 6… The machine does have 16GB of RAM installed, but it could be worth running a test on a later version of java to see if you get better results…
Odd…I’ve never seen this behavior..
July 6, 2007 at 9:30 pm | Anjan Bacchu
hi there,
I ran the following program on a win xp sp2 with 2 GB ram. I gave the VM 700 MB and asked it to allocate 670 MB and it was able to consistenly allocate that memory in 1 chunk. In your case, you are allocating multiple chunks of memory which has its memory managment overheads but I would think that it would not be as bad as you state.
I tested the program in 1.4, 1.5 and 1.6 of sun’s jdk and all of them pretty much behave similarly.
here’s the program.
import java.lang.Runtime;
import java.text.DecimalFormat;
public class Test {
public static void main(String [] args) {
//byte [] array = new byte[1024 * 1024 * 700];
if (args.length == 0) {
usage();
}
final int count = Integer.valueOf(args[0]);
System.out.println(“alloc.value = ” + args[0]);
byte [] array = new byte[count];
System.out.println(“jvm version ” + System.getProperties().getProperty(“java.vm.version”));
System.out.println(“total memory = ” +
(new DecimalFormat(“###,###,###.##”)).format(Runtime.getRuntime().totalMemory())
+ “\nfree memory = ” +
(new DecimalFormat(“###,###,###.##”)).format(Runtime.getRuntime().freeMemory()));
}
private static void fail(String msg) {
System.err.println(msg);
System.exit(-1);
}
private static void usage() {
fail(“ERROR : USAGE Test “);
}
}
July 7, 2007 at 9:44 pm | Hung Huynh
You might want to take a look at Terracotta (http://www.terracotta.org) . It allows you to have virtually unlimited heap size (spilling to disk as needed)
July 8, 2007 at 8:25 am | messi
http://developers.sun.com/mobility/midp/articles/garbagecollection2/#3
July 8, 2007 at 9:16 am | Adam Malter
The problem you are running into is memory fragmentation in the JVM. Arrays need ‘contiguous’ memory for allocation. On our 32 bit Windows machines, this comes out to around 300-400megs, just as you saw. This number decreases as you run and the heap becomes more and more fragmented.
Our solution was to create a ChunkedByteBuffer that allocates data in 64k chunks and throws an interface in front that decorates it as a standard list. Using the chunked methodology we are able allocate 1.2gigs of the 1.4gig available on the heap.
For now the code is buried in our commercial project, but I’ll chat with our project lead if there is any interest in it.
July 9, 2007 at 1:47 am | Henrik S
The Sun JVM requires a contiguous memory space for the heap, this can limit the heap size if you some shared library gets loaded into the address space. BEA JRockit does not have this problem, see:
http://dev2dev.bea.com/blog/hstahl/archive/2005/12/how_to_get_almo.html
A second issue is that each JVM will store some amount of metadata per object for GC, locks etc. This will steal away some heap space. The amount may vary with JVMs, so again you may want to try a different one.
The latest 1.4 version of JRockit can be downloaded here and it is free (as in beer):
http://commerce.bea.com/products/weblogicjrockit/jrockit_prod_fam.jsp
– Henrik
July 9, 2007 at 2:29 pm | Anjan Bacchu
hi Adam Malter/all,
Nice to know about your technique. Can you post some code ? It will be nice to add it to your toolkit.
But, I was successfully able to allocate a single array of 670,000,000(670 MB) bytes when the JVM was given a -Xmx value of 700 MB. And I was using a Sun jdk 1.4.x, 1.5.x and 1.6x.
I can understand that if the heap gets badly defragmented over time, then I might NOT be able to allocate the above chunk.
Any idea why some of us are seeing limitations of 300-400 MB ?
Thanks,
BR,
~A
July 9, 2007 at 4:59 pm | Anjan Bacchu
hi there,
I further noticed that I could allocate upto 1425 MB for the JVM on my windows XP box. The java program itself could allocate 1350MB from the JVM.
I could not increase it beyond that on Windows, though. The ONLY way I know that could increase it beyond that would be to add the /3GB switch to the operating system boot.ini file which should let me allocate about 2.4 GB for the java process.
BR,
~A
July 16, 2007 at 9:20 pm | Infernoz
Memory fragmentation should only rarely occur on a system with an MMU (all current x86 CPUs), the OS should remap the 4KB memory pages into a continuous block and the JVM should take advantage of this. If not, then the OS and JVM designers need a serious kick up the backside!
July 17, 2007 at 8:46 am | Mark Levison
Infernoz – I made the same mistake at first. The problem is not the OS memory but Java’s heap. Java will only hand memory in contiguous chunks which means if the Java heap is fragmented your request will not be granted. The obvious solution to this problem is to allocate your memory in smaller chunks perhaps 10-100,000 int/double chunks.