Large DirectByteBuffer overhead in Util$BufferCache

Solution Verified - Updated

Environment

  • Java
  • JBoss Enterprise Application Platform (EAP)

Issue

  • We see "OutOfMemoryError: Direct buffer memory" errors. The heap dump shows many DirectByteBuffer stored as thread locals by sun.nio.ch.Util$BufferCache:
'- threadLocals java.lang.ThreadLocal$ThreadLocalMap @ 0x52df9aa38       |            1 |           24 |                64 |        41,024
   '- table java.lang.ThreadLocal$ThreadLocalMap$Entry[256] @ 0x51ce090f0|            1 |        1,040 |                64 |        41,000
      '- [184] java.lang.ThreadLocal$ThreadLocalMap$Entry @ 0x52dfc7ef8  |            1 |           32 |                64 |         4,168
         '- value sun.nio.ch.Util$BufferCache @ 0x52dfc7f18              |            1 |           24 |                64 |         4,136
            '- buffers java.nio.ByteBuffer[1024] @ 0x52dfcc510           |            1 |        4,112 |                64 |         4,112
               '- [489] java.nio.DirectByteBuffer @ 0x565d5bb48          |            1 |           64 |                64 |            64

Resolution

Note that this method is intended for simple cases where it is convenient to read all bytes into a byte array. It is not intended for reading in large files.
  • So if handling larger files within this application code, then you should consider adjusting it so that Files.readAllBytes is not used
  • Identify and address other sources of these buffers as identified via byteman
  • The JVM does have a property to cap the peak size of buffers it potentially caches here but it is unbounded by default. So you could consider setting and testing with the following added to your JVM options so then regardless of what ever may create a buffer through Util.getTemporaryDirectBuffer, it will not be cached on a thread if it is above this set max (1 mb in the below example):
-Djdk.nio.maxCachedBufferSize=1048576

Root Cause

Diagnostic Steps

Use byteman to track the source of such buffer creations. To use byteman for this purpose:

  1. Download the latest Byteman release from http://byteman.jboss.org/downloads.html
  2. Unzip byteman into install directory .
  3. Create the Byteman rules file directBuffers.btm with the content below:
RULE log buffer alloctions
CLASS sun.nio.ch.Util
METHOD getTemporaryDirectBuffer
IF true
DO System.out.println ( "getTemporaryDirectBuffer " + $1 );
traceStack();
ENDRULE
  1. Enable the Byteman instrumentation by adding the following to your JVM options:
-javaagent:<mypath>/lib/byteman.jar=script:<mypath>/directBuffers.btm,boot:<mypath>/lib/byteman.jar -Dorg.jboss.byteman.transform.all
  1. Also ensure -Djboss.modules.system.pkgs=org.jboss.byteman is set in your JVM options (it typically is by default)
Components
Category

This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.