Catching java.lang.OutOfMemoryError?

95,261

Solution 1

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 (or already dead) 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.

Solution 2

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.

Solution 3

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

  1. An OOME could also have been thrown on other threads, 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 OOME 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 OOME, 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 OOME, no matter what thread the OOME is thrown on. I'd be interested in opinions on this ...

The only other scenario is when you know for sure that the OOM has not resulted in any collateral damage; i.e. you know:

  • what specifically caused the OOME,
  • what the application was doing at the time, and that it is OK to simply discard that computation, and
  • that a (roughly) simultaneous OOME cannot have occurred on another thread.

There are applications where it is possible to know these things, but for most applications you cannot know for sure that continuation after an OOME is safe. Even if it empirically "works" when you try it.

(The problem is that it a formal proof is required to show that the consequences of "anticipated" OOMEs are safe, and that "unanticipated" OOME's cannot occur within the control of a try/catch OOME.)

Solution 4

Yes, there are real-world scenarios. Here's mine: I need to process data sets of very many items on a cluster with limited memory per node. A given JVM instances goes through many items one after the other, but some of the items are too big to process on the cluster: I can catch the OutOfMemoryError and take note of which items are too big. Later, I can re-run just the large items on a computer with more RAM.

(Because it's a single multi-gigabyte allocation of an array that fails, the JVM is still fine after catching the error and there's enough memory to process the other items.)

Solution 5

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.

Share:
95,261

Related videos on Youtube

Denis Bazhenov
Author by

Denis Bazhenov

Software Developer experienced in developing high traffic web-applications, information retrieval applications and data structures, asynchronous message processing and non relational datasources.

Updated on July 08, 2022

Comments

  • Denis Bazhenov
    Denis Bazhenov almost 2 years

    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 a subclass of java.lang.Throwable, I can catch this type of Throwable.

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

    So, my question is:

    1. Are there any real world scenarios when catching java.lang.OutOfMemoryError might be a good idea?
    2. If we decide to catch java.lang.OutOfMemoryError, how can we make sure the catch handler doesn't allocate any memory by itself (any tools or best practices)?
    • BalusC
      BalusC about 14 years
    • Josep Rodríguez López
      Josep Rodríguez López over 10 years
      For your first question, I'll add that I will catch the OutOfMemoryError in order to (at least try to) notify the user of the problem. Previously, the error wasn't caught by the catch (Exception e) clause, and no feedback was shown to the user.
    • Hot Licks
      Hot Licks almost 10 years
      There are specific cases, eg, allocating a gigantic array, where one can catch the OOM error around that operation and recover reasonably well. But placing the try/catch around a large blob of code and attempting to recover cleanly and continue is probably a bad idea.
    • Raedwald
      Raedwald over 8 years
  • Wolph
    Wolph about 14 years
    Even reserving space it no guarantee for a working solution though. That space might be taken by some other thread too ;)
  • Denis Bazhenov
    Denis Bazhenov about 14 years
    Yes, I agree with you. In general, it is bad idea. But why then I have possibility to catch it? :)
  • BalusC
    BalusC about 14 years
    @Wolph: Then give the JVM more memory! O_o All with all it makes indeed no sense ;)
  • Wolph
    Wolph about 14 years
    Who gave the -1 and why? This is a perfectly valid answer and the only one that offers somewhat of a possible solution.
  • Stephen C
    Stephen C about 14 years
    @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.
  • Denis Bazhenov
    Denis Bazhenov about 14 years
    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?
  • Stephen C
    Stephen C about 14 years
    @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.
  • Tim Bender
    Tim Bender about 14 years
    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.
  • TofuBeer
    TofuBeer about 14 years
    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.
  • Stephen Eilert
    Stephen Eilert about 14 years
    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.
  • Mister Smith
    Mister Smith almost 13 years
    Agree, I'll post my experiment on a new answer.
  • Mister Smith
    Mister Smith almost 13 years
    In a single threaded app, if you are no longer using some of the problematic new objects whose creation throwed the error, these may be collected in the catch clause. However, if the JVM detects that the object may be used later, it can't be collected and the app blows up. See my answer in this thread.
  • Mister Smith
    Mister Smith almost 13 years
    The first snippet works because the object that triggered the error is a single BIG object (the array). When the catch clause is reached, it has been collected by the memory-hungry JVM. If you were using the same object outside the try block, or in other thread, or in the same catch, JVM doesn't collect it, making it impossible to create a new single object of any kind. the second snippet, for instance, may not work.
  • Pacerier
    Pacerier over 12 years
    why not simply setting it to null ?
  • Raedwald
    Raedwald over 12 years
    "you can never be 100% certain that the JVM is in a reparable state": because the OutOfMemoryError might have been thrown from a point that has placed yout program in an inconsistent state, because it can be thrown at any time. See stackoverflow.com/questions/8728866/…
  • Raedwald
    Raedwald over 12 years
    So you have code like byte[] bytes = new byte[length]? Why not just check size at an earlier point?
  • Michael Kuhn
    Michael Kuhn over 12 years
    Because the same size will be fine with more memory. I go via the exception because in most cases everything will be fine.
  • Albert Gan
    Albert Gan about 12 years
    I am not sure myself, but perhaps when this happens, we want to ask our logger to mail this emergency in the catch block ?
  • jontejj
    jontejj over 11 years
    Another option would be to calculate how big bitmaps you can handle instead of trying and failing. "Exceptions should be used for exceptional cases" - I think someone said. But I'll say, your solution seems like the easiest way out, perhaps not the best, but probably the easiest.
  • RoboAlex
    RoboAlex almost 11 years
    In OpenJdk1.7.0_40, I don't get any error or exception when I run this code. Even I changed the MEGABYTE to GIGABYTE (1024*1024*1024). Is that because of optimizer removes the variable 'byte[] bytes' as it is not used in the rest of the code?
  • Stephen C
    Stephen C over 10 years
    "There are a number of scenarios where you may wish to catch an OutOfMemoryError" versus "There is only one good reason to catch an OutOfMemoryError". Make up your mind!!!
  • Pacerier
    Pacerier almost 10 years
    @Chris, Yea Stackoverflow's UI usability isn't exactly top-class.
  • Pacerier
    Pacerier almost 10 years
    @Wolph, Not me, but of course there are many real world scenarios that we may want to catch OOM. For example, saving all the work before shutting down instead of merely just shutting down.
  • Andrew Gallasch
    Andrew Gallasch almost 9 years
    You can't rely on the GC to immediately free that array. Especially in such a fragile state as just after a OOME has been thrown. You couldn't immediately use that memory in the following lines. Could you?
  • Raedwald
    Raedwald over 8 years
    It's even worse than your think. Because an OutOfMemeoryError can be thrown at any point in your program (not only from a new statement) your program will be in an undefined state when you catch the exction.
  • Charles Roth
    Charles Roth about 8 years
    Real-world scenario when you MAY want to catch OutOfMemory error: when it is caused by trying to allocate an array with more than 2G elements. The error name is a bit of a misnomer in this case, but it is still an OOM.
  • maaartinus
    maaartinus about 7 years
    @Andy I guess, he just gave the GC something to reclaim hoping that the GC gets called when needed and that the GC doesn't just give up. No idea if it works and no idea if calling GC manually would make it better or worse.
  • user207421
    user207421 over 6 years
    @MisterSmith Your comment doesn't make sense. The large object doesn't exist. It wasn't allocated in the first place: it triggered the OOM, so it certainly doesn't need GC.
  • user207421
    user207421 over 6 years
    This answer doesn't make sense. If all threads caught OOME, none of them would die, and conversely if none of them do, the JVM won't die just because of one OOME.
  • Stephen C
    Stephen C over 6 years
    It is not a matter of just catching OOMEs. You also need to recover from them. How does a thread recover if it was supposed to (say) notify another thread ... but it got an OOME? Sure, the JVM won't die. But the application is likely to stop working because of threads stuck waiting for notifications from threads that restarted due to catching an OOME.
  • Blaine
    Blaine over 6 years
    to answer #1, here's my scenario: User selects a file, and file is stored in byte array to have some operation done on it. If the user selects too large of a file, the catch will notify them
  • KIC
    KIC about 6 years
    but there it is the real world use case. a catatonic application is a nice example of catch an then system.exit - or not? I am in this state with my tomcat right now. that's why I am here :-)
  • Stephen C
    Stephen C about 6 years
    @KIC - I don't understand what you are saying. Or asking.
  • KIC
    KIC about 6 years
    @StephenC I am using sparkjava (embedded jetty) and a post api. you do not know the size of the payload upfront, yet one payload causes oome which does not kill the vm but put it into a catatonic state. catching the oome and just do System.exit(-1) is kind of a safety belt. Yes, you should never get there in the first place but if so, a catatonic app is worse than a dead one.
  • Stephen C
    Stephen C about 6 years
    OK. Good point. (As I said in my Answer: "In general, it is a bad idea to try to catch and recover from an OOM.")
  • Naanavanalla
    Naanavanalla almost 5 years
    What if I do ll=null inside the catch block?
  • Daniel Alder
    Daniel Alder over 4 years
    It depends on the codec. Imagine a bmp of 10MB probably results in only slightly more than 10MB heap, while a JPEG of 10MB will "explode". Same in my case where I want to parse an XML which can vary massively depending on the complexity of the content