Picture this: It's Black Friday, and you're circling a packed mall parking lot. Every space is taken, and cars are lined up waiting for spots. You keep circling, but there’s just no place to park and you run out of gas.
When you see a java.lang.OutOfMemoryError
it’s just like what you experienced in that overcrowded parking lot. The Java Virtual Machine (JVM) has run out of space to "park" new objects in memory.
Now here's the thing about Java: it loves objects. It can't get enough of them. Objects here, objects there, objects everywhere. But all these objects need a place to park, and that place is called the heap.
When there’s not enough native memory to support the loading of a Java class or the Java Garbage Collector (GC) cannot free up the space in the heap required for a new object, it throws its hands up and yells "OutOfMemoryError!" which is programmer-speak for "We're doomed!" No matter how efficiently your code is written, if there's no room left, you're stuck.
Let’s take a stroll through the heap and figure out how to give your JVM that sweet, sweet memory it craves.
What Causes java.lang.OutOfMemoryError
The JVM's memory management scheme sets aside a portion of the heap memory to store newly allocated objects. Any referenced objects remain active in the heap throughout their lifespan (until their reference is closed) and occupy memory. When objects are no longer referenced, they become eligible for the GC to remove them and free up the occupied heap memory.
The Java heap size is determined by two JVM attributes, which can be set when launching Java:
-Xms
to set the initial heap size-Xmx
to set the maximum heap size
The amount of heap memory used by a Java application impacts the number of objects that can be allocated and their size. If an object requires more memory than is available in the heap, the application encounters a java.lang.OutOfMemoryError
.
A java.lang.OutOfMemoryError
usually means that something is wrong in the application - for example, the application code is referencing large objects for too long or trying to process large amounts of data at a time. The problems could also exist in third-party libraries used within an application.
java.lang.OutOfMemoryError Example
Here is an example of a java.lang.OutOfMemoryError
thrown due to insufficient Java heap space:
public class OutOfMemoryErrorExample {
public static void main(String[] args) {
Integer[] myArray = new Integer[1000 * 1000 * 1000];
}
}
In this example, an Integer
array with a very large size is attempted to be initialized. Because the Java heap is insufficient to allocate this array, it throws a java.lang.OutOfMemoryError: Java heap space
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at OutOfMemoryErrorExample.main(OutOfMemoryErrorExample.java:8)
A java.lang.OutOfMemoryError: Java heap space
can also occur in applications that use finalizers excessively. If a class has a finalize()
method, the GC does not clean up any objects of that class and they are instead queued for finalization, which occurs at a later stage. If the finalizer thread cannot keep up with the finalization queue (due to excessive usage of finalizers), the Java heap space can fill up and a java.lang.OutOfMemoryError
can occur.
How to Catch java.lang.OutOfMemoryError
Since the java.lang.OutOfMemoryError
descends from the Throwable class
, it can be caught and handled in application code. In some cases, especially when the lines of code that may be causing the OutOfMemoryError
are known, it can be a good idea to handle the error. Where it’s possible to do so, it is best practice to clean up the resources, log the reason for the failure and exit the program gracefully. As an example:
public class OutOfMemoryErrorExample {
public void createArray(int size) {
try {
Integer[] myArray = new Integer[size];
} catch (OutOfMemoryError oome) {
//Log the info
System.err.println("Array size too large");
System.err.println("Max JVM memory: " + Runtime.getRuntime().maxMemory());
}
}
public static void main(String[] args) {
OutOfMemoryErrorExample oomee = new OutOfMemoryErrorExample();
oomee.createArray(1000 * 1000 * 1000);
}
}
In this case, because the line of code that may cause an OutOfMemoryError
is known, it is handled in a try-catch block and the reason for the error is logged (the large size of the array) along with the maximum size of the JVM, which helps the caller of the method to take corrective action. The program exits with the following message:
Array size too large
Max JVM memory: 4294967296
It is also a good idea to handle an OutOfMemoryError
when the application needs to be left in a consistent state in case the error occurs. This enables the program to continue running normally if new objects are not attempted to be allocated.
Tips for Avoiding OutOfMemoryError in The First Place
While handling OutOfMemoryErrors is important, preventing them in the first place is even better. Here are five tips to help you do just that:
- Optimize memory usage: Use primitive types instead of wrapper classes when possible and avoid unnecessary object creation. This can significantly reduce memory consumption, especially in large-scale applications.
- Manage resources properly: Always close resources like files and database connections when they're no longer needed. Using try-with-resources can automate this process and prevent resource leaks.
- Use appropriate data structures: Choose efficient data structures based on your use case, and consider using streams for large data processing instead of loading everything into memory at once.
- Tune JVM parameters: Adjust heap size using -Xmx and -Xms flags, and configure garbage collection settings appropriate for your application. This can give your application the memory it needs while optimizing garbage collection.
- Profile your application: Use profiling tools to identify memory leaks and optimize memory usage. Regular profiling can help you catch and fix memory issues before they become critical.
While these tips can significantly reduce the occurrence of OutOfMemoryErrors, no code is perfect. For those times when errors do slip through, having a robust error tracking system like Rollbar can be a game-changer.
Track, Analyze and Manage Java Errors With Rollbar
Managing errors and exceptions in your code is challenging. It can make deploying production code an unnerving experience. Being able to track, analyze, and manage errors in real-time can help you to proceed with more confidence. Rollbar automates Java error monitoring and triaging, making fixing errors easier than ever. Try it today.