Debugging OutOfMemoryError: Direct Buffer Memory Leaks

Jill ThornhillJill Thornhill
4 min read

Are you seeing this JVM error: java.lang.outofmemoryerror: direct buffer memory? It’s becoming more and more common as modern applications move towards using direct buffers to enhance application performance. The Spring framework, for instance, recommends moving from RestTemplates to WebClient, which uses direct buffers.

In this article, we’ll look at what direct buffers are used for, and what might cause them to run out of memory. We’ll also look at some possible solutions to the problem.

What are Direct Buffers?

In Java NIO, buffers can be either direct or indirect. Indirect buffers are stored within the heap, whereas indirect buffers are stored in the Direct Buffer area of native memory.

Fig: JVM memory highlighting Direct Buffer memory

Direct buffers are much faster for both networking and file operations. An example is the ByteBuffer class, which can be instantiated as a direct buffer by using the ByteBuffer.allocateDirect() method.

Error: java.lang.outofmemoryerror: direct buffer memory

This error occurs when the direct buffer area runs out of memory.

The causes include:

  • A memory leak related to direct buffers – usually because the program fails to release buffers when no longer needed.

  • Allocating too many buffers, or making buffers too large;

  • Making the Direct Buffer area too small. The size can be increased using the JVM switch -XX:MaxDirectMemorySize.

It’s often seen when moving from Spring RestTemplate to WebClient without returning the JVM.

For more information about this error, you may like to read: Out of Memory Error: Direct Buffer Memory.

Troubleshooting Direct Buffer Out of Memory Errors

The stack trace following the error message is the best indication of where to look for the problem. Heap analysis tools such as HeapHero won’t show anything abnormal, since the problem affects native memory rather than the heap. If you analyze the garbage collection logs using a tool such as GCeasy, you’re likely to see full garbage collections running more frequently. Eventually, they’re running back-to-back, without having any impact on the heap.

To illustrate this, let’s take a small program that creates one buffer in the heap (indirect buffer), then recursively creates buffers in native memory (direct buffer).

import java.nio.ByteBuffer;

// This program illustrates buffers in both heap and non-heap
// It may result in Direct Memory OOM, depending on JVM settings

public class BuggyProg3 {
   public static void main(String[] args) {
      // Allocate a ByteBuffer in the heap     
         ByteBuffer HeapBuffer = ByteBuffer.allocate(1000000);
      while (true) {
         // Allocate a direct ByteBuffer
         ByteBuffer directBuffer = ByteBuffer.allocateDirect(1000000);
      }
   }
}

If left to run indefinitely, this program may terminate with Error: java.lang.outofmemoryerror: direct buffer memory, depending on the JVM settings.

The diagram below is part of a GCeasy report generated from this program’s GC logs. The small red triangles indicate GC events.

Fig: GCeasy shows continuous GC events without reducing heap size

You can access the full GC report here.

To debug a program that runs out of Direct Buffer memory, check the code to make sure:

  • Buffers are not being created in a method that’s called recursively;

  • Buffers are being released when no longer in use, either by making them local variables in a method that completes in a timely manner, or by explicitly setting the reference to null.

  • Buffers are not too large to fit into the space defined by the JVM switch -XX:MaxDirectMemorySize.

Solutions to java.lang.outofmemoryerror: direct buffer memory

Here are some suggestions for solving out of memory errors in the direct buffer area:

  • Avoid memory leaks by:

  • Setting references to null when no longer needed;

  • Whenever possible, define variables within the method that uses them, rather than as global or static variables;

  • Re-use buffers if possible;

  • Reduce the size of buffers if this is practical. If not, increase the direct buffer size using the JVM switch -XX:MaxDirectMemorySize.

  • If you’re using an older JVM, upgrade to a newer one. Java 17 and later can be up to twice as efficient when using direct buffers.

Conclusion

The error: java.lang.outofmemoryerror: direct buffer memory relates to Direct Buffer memory. This is part of native memory, and therefore not part of the heap. This article has discussed direct buffers, and suggested ways to diagnose and solve this problem.

I hope you found it useful.

0
Subscribe to my newsletter

Read articles from Jill Thornhill directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jill Thornhill
Jill Thornhill