Fixing OutOfMemoryErrors in Java Applications
In this guide, learn how to fix OutOfMemory errors in your Java programs with practical strategies from an experienced programmer.
Join the DZone community and get the full member experience.
Join For FreeIn one of my earlier posts, we discussed how to best find memory leaks and the reasons behind them. It's best to use a focused and modern tool like HeapHero to detect OutOfMemory errors and many other performance bottlenecks, as it can pinpoint the real culprits and suggest ways to optimize the usage of computing resources.
Above, you can see that there are a few thousand objects of byte[]
, String
, int[]
, etc.
Let's discuss some ways of fixing OutOfMemoryErrors
in Java. You can see which fixes are applicable in your scenario/code and apply them to save memory and run your programs better. Some of the ways discussed below may seem trivial to you, but remember that a few small corrections may add up to a big gain.
1. Use ByteBuffer from java.nio
Instead of allocating large byte arrays that may be underutilized, it allows direct memory allocation (ByteBuffer.allocateDirect(size)) to reduce GC pressure and avoid unnecessary heap allocations.
If you are dealing with dynamically growing byte arrays, avoid starting with an unnecessarily large array.
ByteBuffer buffer = ByteBuffer.allocateDirect(1024); // Allocates 1KB in off-heap memory
For example, instead of:
ByteArrayOutputStream baos = new ByteArrayOutputStream(10000);// Too large
Let Java handle the resizing when needed because JVMs and JREs are continuously improved to consume minimal resources and manage their resource cycle well.
ByteArrayOutputStream baos = new ByteArrayOutputStream(); // Starts small and grows as needed
ByteArrayOutputStream baos = new ByteArrayOutputStream(2048); // Starts small and grows as needed
2. Use Streams to Process Data in Chunks
Instead of reading an entire file into memory using a huge byte array,
For example, don’t use:
byte[] data = Files.readAllBytes(Path.of("myLargeFile.txt")); // Loads entire file into memory
Instead, try this:
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("myLargeFile.txt"));
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[2048]; // Read in smaller chunks
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}
byte[] data = baos.toByteArray();
}
3. Using the New MemorySegment Interface in Java 21
You can access off-heap or on-heap memory with the Foreign Function and Memory (FFM) API efficiently. It introduces the concept of an Arena. You use an Arena to allocate a memory segment and control the lifecycle of native memory segments.
SegmentAllocator
from Project Panama (Java 21) allows better control over memory allocation.
Instead of large heap-based arrays, allocate memory using MemorySegment
, which reduces garbage collection overhead.
When you use the try-with-resources
, the Arena will be closed as soon as the try block ends, all memory segments associated with its scope are invalidated, and the memory regions backing them are deallocated.
For example:
import java.lang.foreign.*;
String s = "My LARGE ......... LARGE string";
try (Arena arena = Arena.ofConfined()) {
// Allocate off-heap memory
MemorySegment nativeText = arena.allocateUtf8String(s);
// Access off-heap memory
for (int i = 0; i < s.length(); i++ ) {
System.out.print((char)nativeText.get(ValueLayout.JAVA_BYTE, i));
}
} // Off-heap memory is deallocated
4. Use Singleton Objects Wherever Possible
Some utility classes need not be instantiated per request; there can just be a single static instance for the whole application/session.
For example, Unmarshallers and Marshallers. Unmarshallers are a part of JAXB specification. They are used to convert XML data into Java objects to its XML representations. Similarly, Marshallers are used to convert Java objects into XML representations. These help in processing XML data in Java programs by mapping XML elements and attributes to Java fields and properties, using Java annotations.
If you look closely into the JAXBContext class, you will see that it has static methodscreateUnmarshaller()
/ createMarshaller()
methods, which is a clear indication that these could be better handled as a single static instance for the whole application/session.
5. Use Singleton Scope In Your Spring-Based Applications
This way, the container creates a single instance of that bean for the whole application to share, wherever possible, keeping your business logic intact.
If coding a web application, remember that the @application
scope creates the bean instance for the lifecycle of a ServletContext
, the @request
scope creates a bean instance for a single HTTP request, while the session scope creates a bean instance for a particular HTTP session.
@Bean
@Scope("singleton")
public SomeService someService() {
return new SomeService();
}
6. Use Faster and Memory-Efficient Alternatives to Popular Collections
Use Collections.singletonMap and Collections.singletonList (for Small Collections)
For example, if you only need a single key-value pair or item, avoid using a full HashMap or ArrayList, which have overhead.
Use ArrayDeque Instead of LinkedList
LinkedList has high memory overhead due to storing node pointers (next/prev references). Instead, use ArrayDeque, which is faster and memory-efficient.
import java.util.ArrayDeque;
ArrayDeque<Integer> deque = new ArrayDeque<Integer>();
deque.add(22);
deque.removeFirst();
Use Map.of() and List.of() (Immutable Collections)
If you don't need to modify a collection, use immutable collections, which are compact and optimized.
Map<String, Integer> map = Map.of("A", 1, "B", 2);
List<String> list = List.of("X", "Y", "Z");
Use WeakHashMap for Caching
7. Close Objects as Soon as Their Utility Finishes
Unclosed network sockets, I/O streams, database connections, and database/network objects keep using memory and CPU resources, adding to the running cost of the application.
We have discussed some time-tested ways of dealing with OutOfMemory errors in Java.
Your comments are welcome.
Opinions expressed by DZone contributors are their own.
Comments