I recently came across this in some code – basically someone trying to create a large object, coping when there’s not enough heap to create it:
try { // try to perform an operation using a huge in-memory array byte[] massiveArray = new byte[BIG_NUMBER]; } catch (OutOfMemoryError oome) { // perform the operation in some slower but less // memory intensive way... }
This doesn’t seem right, since Sun themselves recommend that you shouldn’t try to catch Error or its subclasses. We discussed it, and another idea that came up was explicitly checking for free heap:
if (Runtime.getRuntime().freeMemory() > SOME_MEMORY) { // quick memory-intensive approach } else { // slower, less demanding approach }
Again, this seems unsatisfactory – particularly in that picking a value for SOME_MEMORY is difficult to easily relate to the job in question: for some arbitrary large object, how can I estimate how much memory its instantiation might need?
Is there a better way of doing this? Is it even possible in Java, or is any idea of managing memory below the abstraction level of the language itself?
Edit 1: in the first example, it might actually be feasible to estimate the amount of memory a byte[] of a given length might occupy, but is there a more generic way that extends to arbitrary large objects?
Edit 2: as @erickson points out, there are ways to estimate the size of an object once it’s created, but (ignoring a statistical approach based on previous object sizes) is there a way of doing so for yet-uncreated objects?
There also seems to be some debate as to whether it’s reasonable to catch OutOfMemoryError – anyone know anything conclusive?
freeMemory isn’t quite right. You’d also have to add maxMemory()-totalMemory(). e.g. assuming you start up the VM with max-memory=100M, the JVM may at the time of your method call only be using (from the OS) 50M. Of that, let’s say 30M is actually in use by the JVM. That means you’ll show 20M free (roughly, because we’re only talking about the heap here), but if you try to make your larger object, it’ll attempt to grab the other 50M its contract allows it to take from the OS before giving up and erroring. So you’d actually (theoretically) have 70M available.
To make this more complicated, the 30M it reports as in use in the above example includes stuff that may be eligible for garbage collection. So you may actually have more memory available, if it hits the ceiling it’ll try to run a GC to free more memory.
You can try to get around this bit by manually triggering a System.GC, except that that’s not such a terribly good thing to do because
-it’s not guaranteed to run immediately
-it will stop everything in its tracks while it runs
Your best bet (assuming you can’t easily rewrite your algorithm to deal with smaller memory chunks, or write to a memory-mapped file, or something less memory intensive) might be to do a safe rough estimate of the memory needed and insure that it’s available before you run your function.