Troubleshooting OutOfMemoryError in Java
The OutOfMemoryError is a common issue in Java applications, occurring when the JVM cannot allocate enough memory to fulfill a request. The OutOfMemoryError can lead to application crashes and affect overall performance. Proper memory management is critical in preventing it.
This article provides insights and strategies to handle and prevent this error in Java applications, ensuring efficient memory usage and optimal application performance.
1. Java OutOfMemoryError Class
The OutOfMemoryError class is present in ‘java.lang‘ package and has been there since Java version 1.0. It extends VirtualMachineError class; hence, it is an unchecked exception, and we don’t have to declare it in a method or a constructor throws clause.
This issue is more due to the system’s limitation (heap memory space) rather than due to the programming mistakes made by the developer.
Constructors defined in this class:
- OutOfMemoryError(): Creates an instance of the OutOfMemoryError class, setting null as its message.
- OutOfMemoryError(String message): Creates an instance of the OutOfMemoryError class, using the specified message.
2. Various Causes and Fixtures For OutOfMemoryError
OutOfMemoryError occurs when something is wrong in the application either the application code references large-size objects for a longer time than necessary or the user tries to process larger amounts of data in memory at a certain point of time for which JVM won’t allocate sufficient memory.
Let us now dive into some of the main causes for this error and the corresponding fixes for the same:
Problem
In Java, all the new objects are stored in the heap area of JVM, and the heap memory has a limited storage space. This error occurs when heap memory doesn’t have enough space to store new objects.
This can happen for various reasons:
- When we try to process a large amount of data or files which is too large into the application memory to store and process.
- When we keep references to the old objects (ones that we are not using anywhere in our application) and because of this, heap memory piles up with these old objects.
- When we have some programmatic loopholes, and there occurs a memory leak within our application code which fills up the heap memory unnecessarily.
Let us now look at an example to produce a Java heap space problem:
public class JavaHeapSpace < public static void main(String[] args) throws Exception < String[] array = new String[100000 * 100000]; >>
Here, we are trying to create a string array of quite a larger size, and with the default memory settings of JVM, this code on execution will raise OutOfMemoryError. This error indicates that there is not enough memory on the heap to assign this string array.
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at memory.JavaHeapSpace.main(JavaHeapSpace.java:5)
Solution
In most cases, increasing the maximum heap size will resolve this problem so that the JVM can allocate the required amount of memory to the program.
Two main JVM attributes that determine the Java heap size, and we can set them at JVM application startup settings:
For example, to increase the maximum heap size to 8 GB, we must add the -Xmx8g parameter to our JVM start parameters.
This fix will work only till this new memory space wouldn’t gets filled up again. Suppose we try to process some data or files larger than 8 GB then again our application will raise this error or in case our application has memory leaks or useless references then also we will run into the same problem for quite some time again.
For this, we have to go through our application code and find any potential memory leaks and remove useless references from our code to prevent the heap memory from filling up with unnecessary data.
Problem
This error occurs when Garbage Collector won’t able to reclaim memory despite running most of the time, meaning, JVM is spending approx. 98% of its time doing the garbage collection for 5 consecutive garbage collection cycles and still reclaiming less than 2% of the heap space.
We will usually get this error with older java versions with lesser heap memory and older implementations of Garbage collectors like Parallel GC or Serial GC.
Let’s now look at an example to produce GC overhead Limit error:
public class GCOverhead < public static void main(String[] args) throws Exception < Mapmap = new HashMap<>(); for (long i = 0l; i < Long.MAX_VALUE; i++) < map.put(i, i); >> >
When we run the above code with a heap size of 50 MB and using Java version 8 that uses Parallel GC then we will get the below error,
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded at java.base/java.lang.Long.valueOf(Long.java:1211) at memory.GCOverhead.main(GCOverhead.java:10)
Solution
We can resolve this type of error by increasing the heap size using -Xmx attribute so that JVM will have enough heap memory to meet our programming needs and it won’t run GC most of the time to reclaim the memory. We have to also use new implementations of GC that come up with new versions of Java to have better algorithms implemented for garbage collection.
Problem
In a Java application, if we try to create an array of size greater than Integer.MAX_INT or if our array grows too big and ends up with a size greater than the heap memory size then we will get java.lang.OutOfMemoryError: Requested array size exceeds VM limit error.
For example, if an application attempts to allocate an array of size 1024 MB but the maximum heap size is 512 MB then this error occurs.
Solution
If we want to use an array of larger size then we have to increase the heap memory size using -Xmx attribute of JVM. We need to revisit our code as well to remove such situations where our array grows upto heap space, and we should come up with better memory management and data allocation measures in our code.
JVM memory area is divided into 2 parts:
At the time JVM launches, the size of the Heap space and permgen sets up. Heap space stores all the newly created objects in Java whereas PermGen space keeps track of all the loaded classes metadata, static methods and references to static objects.
PermGen space size is limited in Java, we have a PermGen of size approx. 85 MB in a 64-bit version of JVM and because of that if we have lot many classes, static methods or references to static objects in the application then this limited PermGen space fills up and our application will raise java.lang.OutOfMemoryError: PermGen space error.
Since Java version 8, PermGen space removes from the JVM, and hence we won’t get this error anymore with java versions after JDK7.
Solution
We can resolve this error by increasing the size of the PermGen space by including -XX:PermSize and -XX:MaxPermSize JVM parameters.
-XX:PermSize128m -XX:MaxPermSize512m
As the PermGen space has been removed from JVM as part of the Java-8 release, all the class metadata is now been stored in native space also called metaspace which is part of the heap memory of JVM.
This metaspace region is still limited and can be exhausted if we have a lot of classes, resulting in raising java.lang.OutOfMemoryError: Metaspace error.
Solution
We can resolve this error by increasing the size of the Metaspace by adding -XX:MaxMetaspaceSize flag to VM arguments or parameters of our Java application. For example, to set the Metaspace region size to 128M, we would add the following parameter:
Problem
Operating-system level issues cause java.lang.OutOfMemoryError: Out of swap space error, such as:
- When the operating system configures with insufficient swap space.
- When another process on the system consumes all memory resources.
Solution
For resolving this issue, we have to check our operating system swap settings and increase if that is too low. We also need to verify if there are other heavy memory consumers running on the same machine as our application.
Problem
There is a limit to creating and running threads in an application and java.lang.OutOfMemoryError: unable to create native thread error occurs when we hit the thread creation threshold and the operating system runs out of resources to create a new thread for our application.
Let us now look at an example to understand this error more clearly,
public class ThreadsLimits < public static void main(String[] args) throws Exception < while (true) < new Thread( new Runnable() < @Override public void run() < try < Thread.sleep(1000 * 60 * 60 * 24); >catch (Exception ex) <> > > ).start(); > > >
In this program, we continuously create new threads and put them to sleep. After some time, our code exhausts the operating system limits, and it can’t create more threads, and we will end up with the error,
Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached at java.base/java.lang.Thread.start0(Native Method) at java.base/java.lang.Thread.start(Thread.java:802) at memory.ThreadsLimits.main(ThreadsLimits.java:15
Solution
To get rid of these kinds of issues, we should be careful while creating new threads in our application and we should create a new thread only when it’s been required and try to implement Thread Pools so that we can make use of same threads for multiple jobs instead of creating new threads for each and every operation.
3. How to Catch OutOfMemoryError
If a program raises OutOfMemoryError, then being a programmer we can’t do much regarding the lack of memory in our application. It is the system or server admin who can increase the heap memory of the application to resolve this error. But it is highly recommended to handle exceptions wherever it is possible like in cases where the lines of code raising this exception are known then it will be a good idea to handle this error.
To catch this error, we just need to surround the code that may cause memory issues within the try-catch block,
public class OutOfMemoryErrorExample < public void initializeArray (int size) < try < Integer[] arr = new Integer[size]; >catch (OutOfMemoryError e) < //Log the error msg System.err.println("Array size too large :: "+ e); >> public static void main(String[] args) < OutOfMemoryErrorExample obj = new OutOfMemoryErrorExample(); obj.initializeArray(100000 * 100000); >>
Output: Array size too large :: java.lang.OutOfMemoryError: Java heap space
Here, as we know the lines of code that may cause OutOfMemoryError we have kept those in try-catch block so that in case this error occurs then it will be handled gracefully, and the program is terminated/continued normally.
4. Various Tools for Detecting OutOfMemoryError
Detecting OutOfMemoryError is not easy, and one has to investigate a lot to find out the root cause of this error. We can use various tools available in the market that helps us to analyze the objects that are taking a lot of memory in our application, or find any kind of memory leakages within the application. We can also set up alerts or alarming mechanisms as well in our logs which will trigger a notification in case OutOfMemoryError gets logged-in in our application logs.
Some of the popular tools for analyzing OutOfMemoryError are:
VisualVM is another command line JDK tool that monitors and troubleshoots applications running on Java 1.4+. It can monitor the application’s CPU usage, GC activity, heap and metaspace / permanent generation memory, number of loaded classes and running threads.
VisualVM can take and display the thread dumps for an immediate insight of what is going on in the target process to help with uncovering inefficient heap usage and debugging memory leaks.
4.2. Eclipse Memory Analyzer (MAT)
Eclipse Memory Analyzer is an Eclipse foundation tool that is used for analyzing the Java heap dump. It is used for finding the memory leaks and the classloader leaks in our application code.
We can take the heap dump of our application that may contain millions of objects and analyze the same using this tool to detect any memory leakages and identify the places where can reduce the object creations.
Visualgc (Visual Garbage Collection Monitoring Tool) is another popular tool that can be attached to the instrumented hotspot JVM.
It is mainly used to represent all the key data in a graphical manner, including class loader, JVM compiler performance data, and Garbage Collection which we can analyze and figure out the culprits for high memory consumption or any kind of memory leaks.
JProfiler is a popular Java profiler tool in the market. It provides an intuitive UI for viewing system performance, memory usage, potential memory leaks, and thread profiling.
Using this tool, we can easily track performance issues and optimize our Java application. It is a cross-platform tool and various operating systems like Windows, Mac OS, Linux, Solaris, etc. support it. It also supports different IDEs such as NetBeans, Eclipse, IntelliJ, etc.
This tool provides many features such as Memory Profiling, Heap Walker, CPU profiling, Thread Profiling, Databases, etc. to track the performance issue in different sections. It also provides support for both the SQL and NoSQL databases.
5. Conclusion
In this tutorial, we have learned about Java OutOfMemoryError and the answers to the following questions:
- What is OutOfMemoryError class in Java?
- What are the various causes and corresponding fixes for OutOfMemoryError?
- How to catch OutOfMemoryError in our application code?
- Various tools by which we can analyzeOutOfMemoryError and remove memory leaks and improve the application’s performance.