Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8277758

Mixing direct IO and heap byte buffers causes NullPointerException

XMLWordPrintable

    • generic
    • generic

      ADDITIONAL SYSTEM INFORMATION :
      Tested on Mac and Linux with JDK 17.0.1, but probably broken on other platforms / versions as well.

      A DESCRIPTION OF THE PROBLEM :
      Found this bug while adding direct IO support to Elasticsearch. A similar bug was reported by someone else in JDK-8248910, but that one was closed as the test case to reproduce it was using JDK-internal APIs. This bug report has a simple reproducing test case using standard Java APIs (+extended open option for direct IO).

      The gist of the bug is that using heap byte buffers with direct IO causes an aligned direct byte buffer slice to be added to the buffer cache. This byte buffer does not have a cleaner. When another entry is requested from the cache (with a larger size for example), and no suitable buffer found, the previous one is removed and freed. This freeing of the buffer expects a cleaner to be present, and fails with a NullPointerException.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      The steps to reproduce this issue are shown using a simple Java program below

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Program completes successfully
      ACTUAL -
      Exception in thread "main" java.lang.NullPointerException: Cannot invoke "jdk.internal.ref.Cleaner.clean()" because the return value of "sun.nio.ch.DirectBuffer.cleaner()" is null
      at java.base/sun.nio.ch.Util.free(Util.java:328)
      at java.base/sun.nio.ch.Util.getTemporaryDirectBuffer(Util.java:241)
      at java.base/sun.nio.ch.IOUtil.write(IOUtil.java:89)
      at java.base/sun.nio.ch.IOUtil.write(IOUtil.java:67)
      at java.base/sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:285)
      at BugMain.main(BugMain.java:30)


      ---------- BEGIN SOURCE ----------
      import com.sun.nio.file.ExtendedOpenOption;

      import java.io.IOException;
      import java.nio.ByteBuffer;
      import java.nio.channels.FileChannel;
      import java.nio.file.Files;
      import java.nio.file.Paths;
      import java.nio.file.StandardOpenOption;

      public class BugMain {

          public static void main(String... args) throws IOException {
              FileChannel chan1 = FileChannel.open(Paths.get("bla1"),
                  StandardOpenOption.WRITE, StandardOpenOption.CREATE, ExtendedOpenOption.DIRECT);
              FileChannel chan2 = FileChannel.open(Paths.get("bla2"),
                  StandardOpenOption.WRITE, StandardOpenOption.CREATE);

              int blockSize = Math.toIntExact(Files.getFileStore(Paths.get("bla1")).getBlockSize());
              chan1.write(ByteBuffer.allocate(blockSize));
              chan2.write(ByteBuffer.allocate(blockSize + 1));
          }
      }

      ---------- END SOURCE ----------

            bpb Brian Burkhalter
            pnarayanaswa Praveen Narayanaswamy
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: