Simple test case:
$ cat FreeBuffer.java
import sun.nio.ch.Util;
import java.nio.ByteBuffer;
public class FreeBuffer {
public static void main(String[] args) {
// Get a buffer with a capacity of 100, and then get its slice object.
// The slice cleaner is empty.
// And there is no reference to the allocated buffer.
ByteBuffer slice = ByteBuffer.allocateDirect(100).slice();
// Release this slice and it will be in the cache.
Util.releaseTemporaryDirectBuffer(slice);
// Get a buffer with a capacity of 1000.
// At this time, the slice in the cache will be freed by Util.free().
// But the cleaner of this slice is empty.
Util.getTemporaryDirectBuffer(1000);
}
}
$ javac --add-exports=java.base/sun.nio.ch=ALL-UNNAMED FreeBuffer.java
$ java FreeBuffer
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 FreeBuffer.main(FreeBuffer.java:18)
Proposed fix:
diff -r a37f2e2b6fee src/java.base/share/classes/sun/nio/ch/Util.java
--- a/src/java.base/share/classes/sun/nio/ch/Util.java Mon Jul 06 23:11:37 2020 -0700
+++ b/src/java.base/share/classes/sun/nio/ch/Util.java Tue Jul 07 15:20:18 2020 +0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -40,6 +40,7 @@
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.TerminatingThreadLocal;
import jdk.internal.misc.Unsafe;
+import jdk.internal.ref.Cleaner;
import sun.security.action.GetPropertyAction;
public class Util {
@@ -322,10 +323,14 @@
}
/**
- * Frees the memory for the given direct buffer
+ * Frees the memory for the given direct buffer if the buffer has cleaner.
+ * When the buffer has no cleaner, it will be recycled at GC time.
*/
private static void free(ByteBuffer buf) {
- ((DirectBuffer)buf).cleaner().clean();
+ Cleaner cleaner = ((DirectBuffer)buf).cleaner();
+ if (cleaner != null) {
+ cleaner.clean();
+ }
}
$ cat FreeBuffer.java
import sun.nio.ch.Util;
import java.nio.ByteBuffer;
public class FreeBuffer {
public static void main(String[] args) {
// Get a buffer with a capacity of 100, and then get its slice object.
// The slice cleaner is empty.
// And there is no reference to the allocated buffer.
ByteBuffer slice = ByteBuffer.allocateDirect(100).slice();
// Release this slice and it will be in the cache.
Util.releaseTemporaryDirectBuffer(slice);
// Get a buffer with a capacity of 1000.
// At this time, the slice in the cache will be freed by Util.free().
// But the cleaner of this slice is empty.
Util.getTemporaryDirectBuffer(1000);
}
}
$ javac --add-exports=java.base/sun.nio.ch=ALL-UNNAMED FreeBuffer.java
$ java FreeBuffer
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 FreeBuffer.main(FreeBuffer.java:18)
Proposed fix:
diff -r a37f2e2b6fee src/java.base/share/classes/sun/nio/ch/Util.java
--- a/src/java.base/share/classes/sun/nio/ch/Util.java Mon Jul 06 23:11:37 2020 -0700
+++ b/src/java.base/share/classes/sun/nio/ch/Util.java Tue Jul 07 15:20:18 2020 +0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -40,6 +40,7 @@
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.TerminatingThreadLocal;
import jdk.internal.misc.Unsafe;
+import jdk.internal.ref.Cleaner;
import sun.security.action.GetPropertyAction;
public class Util {
@@ -322,10 +323,14 @@
}
/**
- * Frees the memory for the given direct buffer
+ * Frees the memory for the given direct buffer if the buffer has cleaner.
+ * When the buffer has no cleaner, it will be recycled at GC time.
*/
private static void free(ByteBuffer buf) {
- ((DirectBuffer)buf).cleaner().clean();
+ Cleaner cleaner = ((DirectBuffer)buf).cleaner();
+ if (cleaner != null) {
+ cleaner.clean();
+ }
}
- relates to
-
JDK-8277758 Mixing direct IO and heap byte buffers causes NullPointerException
-
- Closed
-