JDK-6857566 : (bf) DirectByteBuffer garbage creation can outpace reclamation
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 6u14,6u24,8
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: linux,windows_7
  • CPU: x86
  • Submitted: 2009-07-06
  • Updated: 2018-07-03
  • Resolved: 2014-02-24
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8 JDK 9
7u201Fixed 8u72Fixed 9 b04Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
1.6.0_12-b04, 1.6.0_14-ea-b03 & 1.7.0-ea-b44

A DESCRIPTION OF THE PROBLEM :
Before throwing an OutOfMemory error for direct memory (allocated using ByteBuffer.allocateDirect), I've noticed that JVM does a full GC to reclaim the direct memory. However, if I have multiple threads trying to allocate direct memory, I get a "java.lang.OutOfMemoryError: Direct buffer memory," even though I know that the garbage collection should have freed up enough direct memory.

In the test case I've attached I have 2 threads which loop, creating 1MB direct byte buffers and then immediately dereferencing them. So I should need a maximum of 2MB of direct memory. However with a 32 MB direct memory size I still get an OutOfMemory error.

When I run the same test case with only 1 thread it passes.

I suspect that maybe the direct memory is being freed up in a java thread, rather than during the garbage collection. I noticed that the JVM creates a DirectByteBuffer$Deallocator object for each of these buffers, which is run by a sun.misc.Cleaner, which I think is run in the reference handler thread.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run "java -Xmx32m -XX:MaxDirectMemorySize=32m test.DirectBufferTest"

ACTUAL -
Thread Test thread 0 got an OOM on iteration 223
java.lang.OutOfMemoryError: Direct buffer memory
	at java.nio.Bits.reserveMemory(Bits.java:633)
	at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:95)
	at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288)
	at test.DirectBufferTest$1.run(DirectBufferTest.java:24)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package test;

import java.nio.ByteBuffer;

public class DirectBufferTest {
  private static int NUM_THREADS = 2;
  
  public static void main(String[] args) throws InterruptedException {
    for(int i = 0; i < NUM_THREADS; i++) {
      Thread thread = new Thread("Test thread " + i) {
        public void run() {
          int i = 0;
          try {
            while(true) {
              ByteBuffer bb = ByteBuffer.allocateDirect(1024 * 1024);
              i++;
            }
          } catch(Throwable t) {
            System.err.println("Thread " + Thread.currentThread().getName() + " got an OOM on iteration " + i);
            t.printStackTrace();
            System.exit(1);
          }
        }
      };
      thread.start();
    }
    
    Thread.sleep(60 * 1000);
    System.out.println("No errors after 60 seconds.");
    System.exit(0);
  }
}
---------- END SOURCE ----------

Comments
Here's the patch, rebased to current jdk9/dev tip: http://cr.openjdk.java.net/~plevart/jdk9-dev/DirectBufferAlloc/webrev.01/ ...Request for Review follows shortly...
04-02-2014

RULE gc/memory/Nio Exception gc.memory.Nio.Nio$Fault: Nop, OOM is unexpected again: java.lang.OutOfMemoryError: Direct buffer memory OpenJDK RI 8b126 http://aurora.ru.oracle.com/functional/faces/RunDetails.xhtml?names=355022.VMSQE.promotion.openjdk-25
04-02-2014

Here's the updated patch which incorporates review suggestions from Aleksey Shipilev and Alan Bateman: http://cr.openjdk.java.net/~plevart/jdk8-tl/DirectBufferAlloc/webrev.02/ Compared to previous patch it incorporates cosmetic changes to code and the test now runs as a jtreg test for at most 5 seconds. It can be run with arguments to increase running time and/or threads to stress-test the code. I have not been able to push the torture tests mentioned by Leonid above to fail on my machine (Linux i7 4-core CPU) even without the patch. Perhaps some special parameters are needed to make them fail with OOME?
20-10-2013

More tests RULE org/openjdk/concurrent/torture/tests/atomicity/buffers/DirectByteBufferAtomicityTests_FloatTest Exception java.lang.OutOfMemoryError: GC overhead limit exceeded RULE org/openjdk/concurrent/torture/tests/atomicity/buffers/DirectByteBufferAtomicityTests_FloatTest Exception java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: GC overhead limit exceeded RULE org/openjdk/concurrent/torture/tests/atomicity/buffers/DirectByteBufferAtomicityTests_FloatTest ExitCode 1 These jcstress (concurrency-torture) tests. This suite was developed by Aleksey Shipilev.
15-10-2013

Affected tests RULE org/openjdk/concurrent/torture/tests/atomicity/buffers/DirectByteBufferAtomicityTests_DoubleTest Exception Exception in thread "main" Exception in thread "worker4" java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: GC overhead limit exceeded RULE org/openjdk/concurrent/torture/tests/atomicity/buffers/DirectByteBufferAtomicityTests_DoubleTest Exception java.lang.OutOfMemoryError: GC overhead limit exceeded RULE org/openjdk/concurrent/torture/tests/atomicity/buffers/DirectByteBufferAtomicityTests_DoubleTest ExitCode 1 RULE org/openjdk/concurrent/torture/tests/atomicity/buffers/DirectByteBufferAtomicityTests_ShortTest Exception java.lang.OutOfMemoryError: GC overhead limit exceeded RULE org/openjdk/concurrent/torture/tests/atomicity/buffers/DirectByteBufferAtomicityTests_ShortTest Exception java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: GC overhead limit exceeded RULE org/openjdk/concurrent/torture/tests/atomicity/buffers/DirectByteBufferAtomicityTests_ShortTest ExitCode 1 RULE org/openjdk/concurrent/torture/tests/atomicity/crosscache/DirectByteBufferIntAtomicityTest Exception java.lang.OutOfMemoryError: Direct buffer memory RULE org/openjdk/concurrent/torture/tests/atomicity/crosscache/DirectByteBufferIntAtomicityTest Exception java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: Direct buffer memory RULE org/openjdk/concurrent/torture/tests/atomicity/crosscache/DirectByteBufferIntAtomicityTest ExitCode 1 RULE org/openjdk/concurrent/torture/tests/tearing/buffers/DirectByteBufferInterleaveTest Exception java.lang.OutOfMemoryError: Direct buffer memory RULE org/openjdk/concurrent/torture/tests/tearing/buffers/DirectByteBufferInterleaveTest Exception java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: Direct buffer memory RULE org/openjdk/concurrent/torture/tests/tearing/buffers/DirectByteBufferInterleaveTest ExitCode 1
14-10-2013

Peter has the latest and greatest patch: http://cr.openjdk.java.net/~plevart/jdk8-tl/DyrectBufferAlloc/webrev.01/
08-10-2013

This is good one to get into an early build of jdk9.
04-10-2013

RFR for the potential fix: http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-October/021547.html
02-10-2013