|
@@ -40,7 +40,7 @@ import java.util.logging.Logger;
|
|
|
|
|
|
/**
|
|
|
* This class contains the reflection based way to remove DirectByteBuffers in
|
|
|
- * java < 9, allocation is done via ByteBuffer.allocateDirect
|
|
|
+ * java, allocation is done via ByteBuffer.allocateDirect
|
|
|
*/
|
|
|
public final class ReflectionAllocator implements BufferAllocator {
|
|
|
private static Method cleanerMethod = null;
|
|
@@ -54,7 +54,7 @@ public final class ReflectionAllocator implements BufferAllocator {
|
|
|
cleanMethod = loadMethod("sun.misc.Cleaner", "clean");
|
|
|
viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "viewedBuffer");
|
|
|
if (viewedBufferMethod == null) {
|
|
|
- // They changed the name in Java 7 (???)
|
|
|
+ // They changed the name in Java 7
|
|
|
viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "attachment");
|
|
|
}
|
|
|
|
|
@@ -72,7 +72,7 @@ public final class ReflectionAllocator implements BufferAllocator {
|
|
|
private static Method loadMethod(String className, String methodName) {
|
|
|
try {
|
|
|
Method method = Class.forName(className).getMethod(methodName);
|
|
|
- method.setAccessible(true);
|
|
|
+ method.setAccessible(true);// according to the Java documentation, by default, a reflected object is not accessible
|
|
|
return method;
|
|
|
} catch (NoSuchMethodException ex) {
|
|
|
return null; // the method was not found
|
|
@@ -80,6 +80,12 @@ public final class ReflectionAllocator implements BufferAllocator {
|
|
|
return null; // setAccessible not allowed by security policy
|
|
|
} catch (ClassNotFoundException ex) {
|
|
|
return null; // the direct buffer implementation was not found
|
|
|
+ } catch (Throwable t) {
|
|
|
+ if (t.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) {
|
|
|
+ return null;// the class is in an exported module
|
|
|
+ } else {
|
|
|
+ throw t;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -96,20 +102,61 @@ public final class ReflectionAllocator implements BufferAllocator {
|
|
|
if (freeMethod != null) {
|
|
|
freeMethod.invoke(toBeDestroyed);
|
|
|
} else {
|
|
|
- Object cleaner = cleanerMethod.invoke(toBeDestroyed);
|
|
|
- if (cleaner != null) {
|
|
|
- cleanMethod.invoke(cleaner);
|
|
|
- } else {
|
|
|
- // Try the alternate approach of getting the viewed buffer
|
|
|
- // first
|
|
|
- Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed);
|
|
|
- if (viewedBuffer != null) {
|
|
|
- destroyDirectBuffer((Buffer) viewedBuffer);
|
|
|
- } else {
|
|
|
- Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE,
|
|
|
- "Buffer cannot be destroyed: {0}", toBeDestroyed);
|
|
|
- }
|
|
|
- }
|
|
|
+ //TODO load the methods only once, store them into a cache (only for Java >= 9)
|
|
|
+ Method localCleanerMethod;
|
|
|
+ if (cleanerMethod == null) {
|
|
|
+ localCleanerMethod = loadMethod(toBeDestroyed.getClass().getName(), "cleaner");
|
|
|
+ } else {
|
|
|
+ localCleanerMethod = cleanerMethod;
|
|
|
+ }
|
|
|
+ if (localCleanerMethod == null) {
|
|
|
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE,
|
|
|
+ "Buffer cannot be destroyed: {0}", toBeDestroyed);
|
|
|
+ } else {
|
|
|
+ Object cleaner = localCleanerMethod.invoke(toBeDestroyed);
|
|
|
+ if (cleaner != null) {
|
|
|
+ Method localCleanMethod;
|
|
|
+ if (cleanMethod == null) {
|
|
|
+ if (cleaner instanceof Runnable) {
|
|
|
+ // jdk.internal.ref.Cleaner implements Runnable in Java 9
|
|
|
+ localCleanMethod = loadMethod(Runnable.class.getName(), "run");
|
|
|
+ } else {
|
|
|
+ // sun.misc.Cleaner does not implement Runnable in Java < 9
|
|
|
+ localCleanMethod = loadMethod(cleaner.getClass().getName(), "clean");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ localCleanMethod = cleanMethod;
|
|
|
+ }
|
|
|
+ if (localCleanMethod == null) {
|
|
|
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE,
|
|
|
+ "Buffer cannot be destroyed: {0}", toBeDestroyed);
|
|
|
+ } else {
|
|
|
+ localCleanMethod.invoke(cleaner);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ Method localViewedBufferMethod;
|
|
|
+ if (viewedBufferMethod == null) {
|
|
|
+ localViewedBufferMethod = loadMethod(toBeDestroyed.getClass().getName(), "viewedBuffer");
|
|
|
+ } else {
|
|
|
+ localViewedBufferMethod = viewedBufferMethod;
|
|
|
+ }
|
|
|
+ if (localViewedBufferMethod == null) {
|
|
|
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE,
|
|
|
+ "Buffer cannot be destroyed: {0}", toBeDestroyed);
|
|
|
+ } else {
|
|
|
+ // Try the alternate approach of getting the viewed
|
|
|
+ // buffer
|
|
|
+ // first
|
|
|
+ Object viewedBuffer = localViewedBufferMethod.invoke(toBeDestroyed);
|
|
|
+ if (viewedBuffer != null) {
|
|
|
+ destroyDirectBuffer((Buffer) viewedBuffer);
|
|
|
+ } else {
|
|
|
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE,
|
|
|
+ "Buffer cannot be destroyed: {0}", toBeDestroyed);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
} catch (IllegalAccessException ex) {
|
|
|
Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
|