Categories
java out-of-memory try-catch

Catching java.lang.OutOfMemoryError?

119

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)?

4

  • 3

  • 1

    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.

    Oct 25, 2013 at 10:14

  • 1

    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.

    – Hot Licks

    Jul 18, 2014 at 11:55

  • See also stackoverflow.com/questions/14376924/…

    – Raedwald

    Apr 2, 2016 at 14:20

97

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.

6

  • Agree, I’ll post my experiment on a new answer.

    Sep 9, 2011 at 7:25

  • 4

    “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

    Jan 10, 2012 at 13:04

  • 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?

    – RoboAlex

    Sep 17, 2013 at 0:47


  • “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!!!

    – Stephen C

    Feb 8, 2014 at 7:47

  • 3

    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.

    May 5, 2016 at 19:02

31

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.

11

  • 2

    Even reserving space it no guarantee for a working solution though. That space might be taken by some other thread too 😉

    – Wolph

    Apr 21, 2010 at 0:50

  • 3

    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.

    Sep 9, 2011 at 8:19


  • 4

    why not simply setting it to null ?

    – Pacerier

    Dec 3, 2011 at 15:47

  • 1

    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?

    Jul 28, 2015 at 22:30

  • 1

    @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.

    Nov 15, 2017 at 22:40


17

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.)

10

  • Yes, I agree with you. In general, it is bad idea. But why then I have possibility to catch it? 🙂

    Apr 21, 2010 at 0:56

  • @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

    Apr 21, 2010 at 2:48

  • 1

    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?

    Apr 21, 2010 at 3:28

  • 1

    @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

    Apr 21, 2010 at 4:02


  • 1

    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.

    – Stephen C

    Nov 15, 2017 at 22:46