views:

726

answers:

9

Documentation for java.lang.Error says:

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch

But as java.lang.Error is subclass of java.lang.Throwable I can catch this type of throwable.

I understand why this is not good idea to catch this sort of exceptions. As far as I understand, if we decide to caught it, the catch handler should not allocate any memory by itself. Otherwise OutOfMemoryError will be thrown again.

So, my question is:

  1. is there any real word scenarios when catching java.lang.OutOfMemoryError may be a good idea?
  2. if we catching java.lang.OutOfMemoryError how can we sure that catch handler doesn't allocate any memory by itself (any tools or best practicies)?

Thanks a lot.

+1  A: 
  1. No, by the time you're getting an OutOfMemoryError your environment is probably already completely wasted so you won't know what is still available and what is not.
  2. You can't in any normal way that I know of. So it probably won't work

If you're really inclined to catching these exceptions than I would opt for a pro-active detection. Detect every once in a while how much memory you have left and if you're getting close to the limit... give a warning/error. Although that too is far from an ideal solution.

WoLpH
A: 
  1. Depends on how you define "good". We do that in our buggy web application and it does work most of the time (thankfully, now OutOfMemory doesn't happen due to an unrelated fix). However, even if you catch it, it still might have broken some important code: if you have several threads, memory allocation can fail in any of them. So, depending on your application there is still 10--90% chance of it being irreversibly broken.
  2. As far as I understand, heavy stack unwinding on the way will invalidate so many references and thus free so much memory you shouldn't care about that.

EDIT: I suggest you try it out. Say, write a program that recursively calls a function that allocates progressively more memory. Catch OutOfMemoryError and see if you can meaningfully continue from that point. According to my experience, you will be able to, though in my case it happened under WebLogic server, so there might have been some black magic involved.

doublep
A: 

You can catch anything under Throwable, generally speaking you should only catch subclasses of Exception excluding RuntimeException (though a large portion of developers also catch RuntimeException... but that was never the intent of the language designers).

If you were to catch OutOfMemoryError what on earth would you do? The VM is out of memory, basically all you can do is exit. You probably cannot even open a dialog box to tell them you are out of memory since that would take memory :-)

The VM throws an OutOfMemoryError when it is truly out of memory (indeed all Errors should indicate unrecoverable situations) and there should really be nothing you can do to deal with it.

The things to do are find out why you are running out of memory (use a profiler, like the one in NetBeans) and make sure you don't have memory leaks. If you don't have memory leaks then increase the memory that you allocate to the VM.

TofuBeer
A misconception your post perpetuates is that OOM indicates the JVM is out of memory. Instead, it actually indicates that the JVM could not allocate all the memory it was instructed to. That is, if the JVM has 10B of space and you 'new up' a 100B object, it will fail, but you could turn around and 'new up' a 5B object and be fine.
Tim Bender
And if I only needed 5B they why am I asking for 10B? If you are doing allocation based on trial and error you are doing it wrong.
TofuBeer
I guess Tim meant that you could still perform some work even with an OutOfMemory situation. There could be enough memory left to open a dialog, for instance.
Stephen Eilert
+4  A: 

In general, it is a bad idea to try to catch and recover from an OOM.

  1. The OOM could also have been thrown on any thread, including threads that your application doesn't even know about. Any such threads will now be dead, and anything that was waiting on a notify could be stuck for ever. In short, your app could be terminally broken.

  2. Even if you do successfully recover, your JVM may still be suffering from heap starvation and your application will perform abysmally as a result.

The best thing to do with an OOM is to let the JVM die.

(This assumes that the JVM does die. For instance OOMs on a Tomcat servlet thread do not kill the JVM, and this leads to the Tomcat going into a catatonic state where it won't respond to any requests ... not even requests to restart.)

EDIT

I am not saying that it is a bad idea to catch OOM at all. The problems arise when you then attempt to recover from the OOM, either deliberately or by oversight. Whenever you catch an OOM (directly, or as a subtype of Error or Throwable) you should either rethrow it, or arrange that the application / JVM exits.

Aside: This suggests that for maximum robustness in the face of OOMs an application should use Thread.setDefaultUncaughtExceptionHandler() to set a handler that will cause the application to exit in the event of an OOM, no matter what thread the OOM is thrown on. I'd be interested in opinions on this ...

Stephen C
Yes, I agree with you. In general, it is bad idea. But why then I have possibility to catch it? :)
dotsid
@dotsid - 1) because there are cases where you should catch it, and 2) because making it impossible to catch OOM would have negative impact on the language and/or other parts of the Java runtime.
Stephen C
@Stephen C. You say: "because there are cases where you should catch it". So this was part of my original question. *What* are the cases when you want to catch OOME?
dotsid
@dotsid - see my edited answer. The only case I can think of for catching OOM is when you need to do this to *force* a multi-threaded application to exit in the event of an OOM. You maybe want to do this for all subtypes of `Error`.
Stephen C
A: 

The only reason i can think of why catching OOM errors could be that you have some massive data structures you're not using anymore, and can set to null and free up some memory. But (1) that means you're wasting memory, and you should fix your code rather than just limping along after an OOME, and (2) even if you caught it, what would you do? OOM can happen at any time, potentially leaving everything half done.

cHao
Well, we have "WeakReference and friends" for this kind of behavior...
dotsid
+3  A: 

You can recover from it:

package com.stackoverflow.q2679330;

public class Test {

    public static void main(String... args) {
        int size = Integer.MAX_VALUE;
        int factor = 10;

        while (true) {
            try {
                System.out.println("Trying to allocate " + size + " bytes");
                byte[] bytes = new byte[size];
                System.out.println("Succeed!");
                break;
            } catch (OutOfMemoryError e) {
                System.out.println("OOME .. Trying again with 10x less");
                size /= factor;
            }
        }
    }

}

But does it make sense? What else would you like to do? Why would you initially allocate that much of memory? Is less memory also OK? Why don't you already make use of it anyway? Or if that's not possible, why not just giving the JVM more memory from the beginning on?

Back to your questions:

1: is there any real word scenarios when catching java.lang.OutOfMemoryError may be a good idea?

None comes to mind.

2: if we catching java.lang.OutOfMemoryError how can we sure that catch handler doesn't allocate any memory by itself (any tools or best practicies)?

Depends on what has caused the OOME. If it's declared outside the try block and it happened step-by-step, then your chances are little. You may want to reserve some memory space beforehand:

private static byte[] reserve = new byte[1024 * 1024]; // Reserves 1MB.

and then set it to zero during OOME:

} catch (OutOfMemoryException e) {
     reserve = new byte[0];
     // Ha! 1MB free!
}

Of course this makes all with all no sense ;) Just give JVM sufficient memory as your applictation require. Run a profiler if necessary.

BalusC
Even reserving space it no guarantee for a working solution though. That space might be taken by some other thread too ;)
WoLpH
@Wolph: Then give the JVM more memory! O_o All with all it makes indeed no sense ;)
BalusC
Who gave the -1 and why? This is a perfectly valid answer and the only one that offers somewhat of a possible solution.
WoLpH
Very good point. Thanks.
dotsid
+1  A: 

Yes, the real question is "what are you going to do in the exception handler?" For almost anything useful, you'll allocate more memory. If you'd like to do some diagnostic work when an OutOfMemoryError occurs, you can use the -XX:OnOutOfMemoryError=<cmd> hook supplied by the HotSpot VM. It will execute your command(s) when an OutOfMemoryError occurs, and you can do something useful outside of Java's heap. You really want to keep the application from running out of memory in the first place, so figuring out why it happens is the first step. Then you can increase the heap size of the MaxPermSize as appropriate. Here are some other useful HotSpot hooks:

-XX:+PrintCommandLineFlags
-XX:+PrintConcurrentLocks
-XX:+PrintClassHistogram

See the full list here

Rob Heiser
+2  A: 

There are definitely scenarios where catching an OOME makes sense. IDEA catches them and pops up a dialog to let you change the startup memory settings (and then exits when you are done). An application server might catch and report them. The key to doing this is to do it at a high level on the dispatch so that you have a reasonable chance of having a bunch of resources freed up at the point where you are catching the exception.

Besides the IDEA scenario above, in general the catching should be of Throwable, not just OOM specifically, and should be done in a context where at least the thread will be terminated shortly.

Of course most times memory is starved and the situation is not recoverable, but there are ways that it makes sense.

Yishai
GUI notifications is a good point, I think. Thanks.
dotsid
+2  A: 

I agree and disagree with most the responses here.

There are a number of scenarios where you may wish to catch an OutOfMemoryError and in my experience (on Windows and Solaris JVMs), only very infrequently is OutOfMemoryError the death-knell to a JVM.

There is only one good reason to catch an OutOfMemoryError and that is to close down gracefully, cleanly releasing resources and logging the reason for the failure best you can (if it is still possible to do so).

In general, the OutOfMemoryError occurs due to a block memory allocation that cannot be satisfied with the remaining resources of the heap.

When the Error is thrown the heap contains the same amount of allocated objects as before the unsuccessful allocation and now is the time to drop references to run-time objects to free even more memory that may be required for cleanup. In these cases, it may even be possible to continue but that would definitely be a bad idea as you can never be 100% certain that the JVM is in a reparable state.

Demonstration that OutOfMemoryError does not mean that the JVM is out of memory in the catch block:

private static final int MEGABYTE = (1024*1024);
public static void runOutOfMemory() {
    MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
    for (int i=1; i <= 100; i++) {
        try {
            byte[] bytes = new byte[MEGABYTE*500];
        } catch (Exception e) {
            e.printStackTrace();
        } catch (OutOfMemoryError e) {
            MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
            long maxMemory = heapUsage.getMax() / MEGABYTE;
            long usedMemory = heapUsage.getUsed() / MEGABYTE;
            System.out.println(i+ " : Memory Use :" + usedMemory + "M/" + maxMemory + "M");
        }
    }
}

Output of this code:

1 : Memory Use :0M/247M
..
..
..
98 : Memory Use :0M/247M
99 : Memory Use :0M/247M
100 : Memory Use :0M/247M

If running something critical, I usually catch the Error, log it to syserr, then log it using my logging framework of choice, then proceed to release resources and close down in a clean fashion. What's the worst that can happen? The JVM is dying anyway and by catching the Error there is at least a chance of cleanup.

The caveat is that you have to target the catching of these types of errors only in places where cleanup is possible. Don't blanket catch(Throwable t) {} everywhere or nonsense like that.

Chris
Oops"be a bad idea as you can be 100%" should read "be a bad idea as you can NEVER be 100%"
Chris
Even with low reputation, you can still edit your own answer at will. Just click the edit link on the bottom left of the answer.
Vincent Robert
Thanks for the info and the edit. I did look for the edit button but I must be selectively blind.
Chris