Large DirectByteBuffer overhead in Util$BufferCache
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
- Per the Content from docs.oracle.com is not included.Docs on Files.readAllBytes, note this call is not recommended for larger file reads:
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
sun.nio.ch.Util.getTemporaryDirectBufferis called to generate the buffer, which then caches it as thread local upon return.java.nio.file.Files.readAllBytesis called for large files, which usessun.nio.ch.Util.getTemporaryDirectBuffer- RESTEasy client calls on EAP 7.4.22+ produce large DirectByteBuffer overhead in Util$BufferCache
Diagnostic Steps
Use byteman to track the source of such buffer creations. To use byteman for this purpose:
- Download the latest Byteman release from http://byteman.jboss.org/downloads.html
- Unzip byteman into install directory
. - 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
- 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
- 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.