How to Catch Out of Memory Exception in Java

Is it possible to catch out of memory exception in java?

It's not an exception; it's an error: java.lang.OutOfMemoryError

You can catch it as it descends from Throwable:

try {
// create lots of objects here and stash them somewhere
} catch (OutOfMemoryError E) {
// release some (all) of the above objects
}

However, unless you're doing some rather specific stuff (allocating tons of things within a specific code section, for example) you likely won't be able to catch it as you won't know where it's going to be thrown from.

Correct place to catch out of memory error

The correct place to catch any Exception or Error in Java is the place where you have a mechanism to handle them and perform some recovery steps. In the case of OutOfMemoryError, you should catch the error ONLY when you are able to to close it down gracefully, cleanly releasing resources and logging the reason for the failure, if possible.

OutOfMemoryError occurs due to a block memory allocation that cannot be satisfied with the remaining resources of the heap. Whenever OutOfMemoryError is thrown, the heap contains the exact same number of allocated objects before the unsuccessful attempt of allocation. This should be the actual time when you should catch the OutOfMemoryError and attempt to drop references to run-time objects to free even more memory that may be required for cleanup.

If the JVM is in reparable state, which you can never determine it through the program, it is even possible to recover & continue from the error. But this is generally considered as a not good design as I said you can never determine it through the program.

If you see the documentation of java.lang.Error, it says

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

If you are catching any error on purpose, please remember NOT to blanket catch(Throwable t) {...} everywhere in your code.

More details here.

Catching java.lang.OutOfMemoryError?

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.

Java try catch - out of memory

How good is a try catch at catching out of memory exceptions?

It works just fine at one level ... but at another level it can be risky, and / or futile. See below.

Is it possible that the program managing memory runs out of memory?

The "program" managing memory is the garbage collector. AFAIK, it always has enough memory for its own purposes. Though if it didn't it would have no alternative but to hard crash the JVM.

When could I try to catch an out of memory exception and it would fail to catch the exception?

Only if the JVM crashes. Or, if the OOME is thrown on a different thread stack to the one where you are trying to catch it.


OK, so why is catching OOME risky and / or futile.

The first reason is that an OOME can be thrown on any thread stack without any warning. When you catch the exception, the handler (typically) has no way to know what was happening "up stack", and exactly how it failed. Hence, it has no way of knowing if the application's execution state has been damaged or compromised. (Was the thread in the middle of updating something important? Was it about to notify some other thread ... and the thread will now never get the notification?)

The second reason is that OOMEs are often a result of a Java storage leak ... caused by something "hang onto" objects when it shouldn't do. If you catch an OOME and attempt to resume ... when the problem was caused by a leak ... the chances are that the offending objects will still be reachable, and another OOME will follow pretty soon. In other words, your application could get stuck in a state where it is continually throwing and recovering from OOMEs. At best this is going to kill performance ... because the last thing that the JVM typically does before an OOME is to perform a full (stop the world) garbage collection. And that takes a significant time.

Note that is not to say that you should never catch OOMEs. Indeed, catching an OOME, reporting it and then shutting down is often a good strategy.

No, the thing that is risky / futile is to catch the OOME and then attempt to recover and continue running.

Trying to understand the Java Out Of Memory Error and catching the throwable

This is the meat of the code:

try {
Integer[] array = new Integer[10000000 * 10000000];
} catch (Throwable e) {
/* ... */
}
int arr[] = new int[100];

According to the output in your question, this is failing in the large allocation, recovering and succeeding with the small allocation.

Why this program is able to run further, even it got out of memory error.

Short answer: because the JVM is designed so that it can recover from OOMEs in most circumstances.

In your example, the first new operator will result in a memory allocation request for a large amount of memory (see below). The memory allocator will notice that the request is larger than what is available. It will then (typically) trigger a full garbage collector to free up as much memory as possible, then repeat the request.

In this example, there is still not enough memory for the very large allocation. The allocator then throws an OutOfMemoryError which is then propagated like any other exception. At this point in time, there will be lots of free memory ... but just not enough for the huge allocation.

Then your code catches the OOME, and tries to allocate a much smaller array. That succeeds, since the memory is available.

Your test application does this sequence repeatedly, and it behaves the same say each time.

Why this program is able to run further, even it got out of memory error.

In your example, the JVM is only "out of memory" for the huge allocation request. For a smaller request, there is no problem.

Note that the huge new did not actually allocate memory. It tested to see if it could do the allocation, and decided it couldn't.


Basically, your "theory" is incorrect. It is however generally a bad idea to attempt to catch and recover from OOMEs. Here are some reasons:

  1. OOMEs are liable to cause threads to terminate unexpectedly, leaving data structures in inconsistent states, notifications that won't be sent and so on. Recovery can be problematic.

  2. If recovery leads to the same request being repeated, you are liable to get the OOME repeated.

  3. If the root cause of the OOME's is a memory leak, then recovering from an OOME is unlikely to fix the memory leak. So recovery leads to the OOMEs increasing in frequency until the system grinds to a halt.

Of course, there are exceptions to all of the above.


There are a couple of other things to note.

  1. Your allocation appears to be requesting an array with 10,000,000 x 10,000,000 elements. However that is not actually what happens. In fact, 10000000 * 10000000 is an int expression. It overflows, and the result is truncated to 276447232. That is still very large, especially because you need to multiply by 8 to get the approximate array size in bytes.

  2. Since your JVM's max heap size is only 1MB, it is likely that the allocator won't bother to run GC. It would make no difference to the outcome. However, if there was any chance that the GC could free enough memory, you can be assured that it will be run before the allocator gives up and throws an OOME.

  3. The JVM's memory manager splits the heap into a number of spaces. The details depend on the GC that you have selected. When a really large object is requested, the allocator has to find a contiguous area of memory that fits in one space. This can be impossible ... even though the aggregate free space should be large enough.

What is the root cause of getting Out Of Memory Exception? How can we overcome this?

  1. What are the possible scenarios with the above snippet to get "out of memory exception" ?

There are various root causes for out of memory exceptions. Refer to oracle documentation page for more details.

java.lang.OutOfMemoryError: Java heap space:

Cause: The detail message Java heap space indicates object could not be allocated in the Java heap.

java.lang.OutOfMemoryError: GC Overhead limit exceeded:

Cause: The detail message "GC overhead limit exceeded" indicates that the garbage collector is running all the time and Java program is making very slow progress

java.lang.OutOfMemoryError: Requested array size exceeds VM limit:

Cause: The detail message "Requested array size exceeds VM limit" indicates that the application (or APIs used by that application) attempted to allocate an array that is larger than the heap size.

java.lang.OutOfMemoryError: Metaspace:

Cause: Java class metadata (the virtual machines internal presentation of Java class) is allocated in native memory (referred to here as metaspace)

java.lang.OutOfMemoryError: request size bytes for reason. Out of swap space?:

Cause: The detail message "request size bytes for reason. Out of swap space?" appears to be an OutOfMemoryError exception. However, the Java HotSpot VM code reports this apparent exception when an allocation from the native heap failed and the native heap might be close to exhaustion


  1. Is it necessary to close the output stream here? And does stream, flush is enough or do we need to close the stream always? If so why?

since you are using raw InputStream and OutputStream in your method, we don' t know which type of actual Stream is getting passed to this method and hence explicitly close these Streams is good idea.


  1. How could I avoid Out of memory exception in general?

This question is already answered in response to your first question.

Refer to this SE question on handling large files for IO operations :

Java OutOfMemoryError in reading a large text file

Recover from java.lang.OutOfMemoryError

bro to catch the outofmemoryerror just replace Exception in catch with OutOfMemoryError

catch(OutOfMemoryError e)

Java OutOfMemoryError not caught by clauses that catch Error and Throwable

Inside of your exception handler you're trying to allocate the string "OutOfMemoryError=<" + oome + ">", but you're out of memory and so this is probably going to throw another OutOfMemoryError



Related Topics



Leave a reply



Submit