/** * Copyright (c) 2006-2016 Julien Gouesse This program is free software; you can * redistribute it and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. This program is distributed * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. You should have received * a copy of the GNU General Public License along with this program; if not, * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ package engine.misc; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.CharBuffer; import java.nio.DoubleBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.LongBuffer; import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import net.i2p.I2PAppContext; import net.i2p.util.Log; /** * Helper to deallocate memory on the native heap allocated during the creation * of a direct byte buffer. It supports numerous virtual machines including * OpenJDK, Oracle/Sun Java, Android Dalvik Virtual Machine, Apache Harmony and * GNU Classpath. This class uses the syntax of Java 1.7 but it can work * correctly with Java 1.4 with a very few minor type changes when using the * maps and the collections. It relies on lots of implementation details but * it's robust enough to go on working (except when the implementors * intentionally use a very general class to store the buffers) despite minor * naming changes like those that occurred between Java 1.6 and Java 1.7. It * supports Java 1.9 despite the move of the cleaner from the package sun.misc * to jdk.internal.ref (in the module java.base). N.B: Releasing the native * memory of a sliced direct NIO buffer, the one of a direct NIO buffer created * with JNI or the one of any direct NIO buffer created by the virtual machine * or by a framework not under your control doesn't prevent the calls to methods * attempting to access such buffers. Those calls can throw an exception or * crash the virtual machine depending on the implementations. * * @author Julien Gouesse */ public class DeallocationHelper { private final Log logger = I2PAppContext.getGlobalContext().logManager().getLog(DeallocationHelper.class); /** * tool responsible for releasing the native memory of a deallocatable byte * buffer */ public static abstract class Deallocator { protected final Log logger = I2PAppContext.getGlobalContext().logManager().getLog(DeallocationHelper.class); public Deallocator() { super(); } /** * releases the native memory of a deallocatable byte buffer * * @param directByteBuffer * deallocatable byte buffer * * @return <code>true</code> if the deallocation is successful, * otherwise <code>false</code> */ public abstract boolean run(final ByteBuffer directByteBuffer); } public static class OracleSunOpenJdkDeallocator extends Deallocator { private Method directByteBufferCleanerMethod; private Method cleanerCleanMethod; public OracleSunOpenJdkDeallocator() { super(); try { final Class<?> directByteBufferClass = Class.forName("java.nio.DirectByteBuffer"); directByteBufferCleanerMethod = directByteBufferClass.getDeclaredMethod("cleaner"); /** * The return type is sun.misc.Cleaner in Java <= 1.8, * jdk.internal.ref.Cleaner in Java >= 1.9. Only the latter * implements the Runnable interface. */ final Class<?> cleanerClass = directByteBufferCleanerMethod.getReturnType(); if (Runnable.class.isAssignableFrom(cleanerClass)) { cleanerCleanMethod = Runnable.class.getDeclaredMethod("run"); } else { cleanerCleanMethod = cleanerClass.getDeclaredMethod("clean"); } } catch (ClassNotFoundException | NoSuchMethodException e) { logger.warn( "The initialization of the deallocator for Oracle Java, Sun Java and OpenJDK has failed", e); } } @Override public boolean run(final ByteBuffer directByteBuffer) { boolean success = false; if (directByteBufferCleanerMethod != null && cleanerCleanMethod != null) { final boolean directByteBufferCleanerMethodWasAccessible = directByteBufferCleanerMethod.isAccessible(); final boolean cleanerCleanMethodWasAccessible = cleanerCleanMethod.isAccessible(); try { // according to the Java documentation, by default, a reflected object is not accessible directByteBufferCleanerMethod.setAccessible(true); final Object cleaner = directByteBufferCleanerMethod.invoke(directByteBuffer); if (cleaner != null) { cleanerCleanMethod.setAccessible(true); cleanerCleanMethod.invoke(cleaner); success = true; } } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { logger.warn("The deallocation of a direct NIO buffer has failed", e); } finally { directByteBufferCleanerMethod.setAccessible(directByteBufferCleanerMethodWasAccessible); cleanerCleanMethod.setAccessible(cleanerCleanMethodWasAccessible); } } return (success); } } public static class AndroidDeallocator extends Deallocator { private Method directByteBufferFreeMethod; public AndroidDeallocator() { super(); try { final Class<?> directByteBufferClass = Class.forName("java.nio.DirectByteBuffer"); directByteBufferFreeMethod = directByteBufferClass.getDeclaredMethod("free"); } catch (ClassNotFoundException | NoSuchMethodException e) { logger.warn("The initialization of the deallocator for Android has failed", e); } } @Override public boolean run(final ByteBuffer directByteBuffer) { boolean success = false; if (directByteBufferFreeMethod != null) { final boolean directByteBufferFreeMethodWasAccessible = directByteBufferFreeMethod.isAccessible(); try { directByteBufferFreeMethod.setAccessible(true); directByteBufferFreeMethod.invoke(directByteBuffer); success = true; } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { logger.warn("The deallocation of a direct NIO buffer has failed", e); } finally { directByteBufferFreeMethod.setAccessible(directByteBufferFreeMethodWasAccessible); } } return (success); } } public static class GnuClasspathDeallocator extends Deallocator { private Method vmDirectByteBufferFreeMethod; private Field bufferAddressField; public GnuClasspathDeallocator() { super(); try { final Class<?> vmDirectByteBufferClass = Class.forName("java.nio.VMDirectByteBuffer"); final Class<?> gnuClasspathPointerClass = Class.forName("gnu.classpath.Pointer"); vmDirectByteBufferFreeMethod = vmDirectByteBufferClass.getDeclaredMethod("free", gnuClasspathPointerClass); bufferAddressField = Buffer.class.getDeclaredField("address"); } catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException e) { logger.warn("The initialization of the deallocator for GNU Classpath has failed", e); } } @Override public boolean run(final ByteBuffer directByteBuffer) { boolean success = false; if (vmDirectByteBufferFreeMethod != null && bufferAddressField != null) { final boolean bufferAddressFieldWasAccessible = bufferAddressField.isAccessible(); final boolean vmDirectByteBufferFreeMethodWasAccessible = vmDirectByteBufferFreeMethod.isAccessible(); try { bufferAddressField.setAccessible(true); final Object address = bufferAddressField.get(directByteBuffer); if (address != null) { vmDirectByteBufferFreeMethod.setAccessible(true); vmDirectByteBufferFreeMethod.invoke(null, address); success = true; } } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { logger.warn("The deallocation of a direct NIO buffer has failed", e); } finally { bufferAddressField.setAccessible(bufferAddressFieldWasAccessible); vmDirectByteBufferFreeMethod.setAccessible(vmDirectByteBufferFreeMethodWasAccessible); } } return (success); } } public static class ApacheHarmonyDeallocator extends Deallocator { private Method directByteBufferFreeMethod; public ApacheHarmonyDeallocator() { super(); try { final Class<?> directByteBufferClass = Class.forName("java.nio.DirectByteBuffer"); directByteBufferFreeMethod = directByteBufferClass.getDeclaredMethod("free"); } catch (ClassNotFoundException | NoSuchMethodException e) { logger.warn("The initialization of the deallocator for Apache Harmony has failed", e); } } @Override public boolean run(final ByteBuffer directByteBuffer) { boolean success = false; if (directByteBufferFreeMethod != null) { final boolean directByteBufferFreeMethodWasAccessible = directByteBufferFreeMethod.isAccessible(); try { directByteBufferFreeMethod.setAccessible(true); directByteBufferFreeMethod.invoke(directByteBuffer); success = true; } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { logger.warn("The deallocation of a direct NIO buffer has failed", e); } finally { directByteBufferFreeMethod.setAccessible(directByteBufferFreeMethodWasAccessible); } } return (success); } } private Map<Class<?>, Field> attachmentOrByteBufferFieldMap; private Set<Class<?>> deallocatableBufferClassSet; private Deallocator deallocator; /** * Default constructor */ public DeallocationHelper() { this(false); } /** * Main constructor * * @param ignoreClassesAndFieldsHints * <code>true</code> if the known implementation details should * be ignored when looking for the classes and the fields used * for the native memory of the direct buffers (they are then * fully recomputed at runtime which is slower but safer), * otherwise <code>false</code> */ public DeallocationHelper(final boolean ignoreClassesAndFieldsHints) { super(); final List<Buffer> buffersToDelete = new ArrayList<>(); /** * builds the map used to determine the names of the fields containing * the direct byte buffers. The direct read only buffers and the sliced * buffers and the direct buffers for other primitive types than bytes * store their data into some direct byte buffers. Those direct byte * buffers often are the only one accessing directly to the native * memory. That's why it's necessary to find them when a developer * passes a direct NIO buffer. The code below relies on numerous * implementation details found in some classes not available in the * public APIs, it's used to find the fields faster in most of the * cases. The class names haven't changed since Java 1.4 unlike a few * field names. */ final Map<String, String> attachmentOrByteBufferFieldNameMap = new HashMap<>(); final String javaVendor = System.getProperty("java.vendor"); final String javaVersion = System.getProperty("java.version"); if (!ignoreClassesAndFieldsHints) { if (javaVendor.equals("Sun Microsystems Inc.") || javaVendor.equals("Oracle Corporation")) { final String java14to16DirectBufferAttachmentFieldName = "viewedBuffer"; final String java17to19DirectBufferAttachmentFieldName = "att"; final String byteBufferAsNonByteBufferByteBufferFieldName = "bb"; final String[] directBufferClassnames = new String[] { "java.nio.DirectByteBuffer", "java.nio.DirectByteBufferR", "java.nio.DirectCharBufferRS", "java.nio.DirectCharBufferRU", "java.nio.DirectCharBufferS", "java.nio.DirectCharBufferU", "java.nio.DirectDoubleBufferRS", "java.nio.DirectDoubleBufferRU", "java.nio.DirectDoubleBufferS", "java.nio.DirectDoubleBufferU", "java.nio.DirectFloatBufferRS", "java.nio.DirectFloatBufferRU", "java.nio.DirectFloatBufferS", "java.nio.DirectFloatBufferU", "java.nio.DirectIntBufferRS", "java.nio.DirectIntBufferRU", "java.nio.DirectIntBufferS", "java.nio.DirectIntBufferU", "java.nio.DirectLongBufferRS", "java.nio.DirectLongBufferRU", "java.nio.DirectLongBufferS", "java.nio.DirectLongBufferU", "java.nio.DirectShortBufferRS", "java.nio.DirectShortBufferRU", "java.nio.DirectShortBufferS", "java.nio.DirectShortBufferU" }; final String[] byteBufferAsNonByteBufferClassnames = new String[] { "java.nio.ByteBufferAsCharBufferB", "java.nio.ByteBufferAsCharBufferL", "java.nio.ByteBufferAsCharBufferRB", "java.nio.ByteBufferAsCharBufferRL", "java.nio.ByteBufferAsDoubleBufferB", "java.nio.ByteBufferAsDoubleBufferL", "java.nio.ByteBufferAsDoubleBufferRB", "java.nio.ByteBufferAsDoubleBufferRL", "java.nio.ByteBufferAsFloatBufferB", "java.nio.ByteBufferAsFloatBufferL", "java.nio.ByteBufferAsFloatBufferRB", "java.nio.ByteBufferAsFloatBufferRL", "java.nio.ByteBufferAsIntBufferB", "java.nio.ByteBufferAsIntBufferL", "java.nio.ByteBufferAsIntBufferRB", "java.nio.ByteBufferAsIntBufferRL", "java.nio.ByteBufferAsLongBufferB", "java.nio.ByteBufferAsLongBufferL", "java.nio.ByteBufferAsLongBufferRB", "java.nio.ByteBufferAsLongBufferRL", "java.nio.ByteBufferAsShortBufferB", "java.nio.ByteBufferAsShortBufferL", "java.nio.ByteBufferAsShortBufferRB", "java.nio.ByteBufferAsShortBufferRL" }; final String[] javaVersionElements = System.getProperty("java.version").split("\\."); int indexOfEarlyAccessSuffix = javaVersionElements[0].lastIndexOf("-ea"); if (indexOfEarlyAccessSuffix != -1) { // drops the "-ea" suffix from the major version number for // an early access build javaVersionElements[0] = javaVersionElements[0].substring(0, indexOfEarlyAccessSuffix); } else { indexOfEarlyAccessSuffix = javaVersionElements[0].lastIndexOf("-internal"); if (indexOfEarlyAccessSuffix != -1) { // drops the "-internal" suffix from the major version number for // an early access build (Ubuntu) javaVersionElements[0] = javaVersionElements[0].substring(0, indexOfEarlyAccessSuffix); } else { indexOfEarlyAccessSuffix = javaVersionElements[0].lastIndexOf("-Ubuntu"); if (indexOfEarlyAccessSuffix != -1) { // drops the "-Ubuntu suffix from the major version number for // an early access build (Ubuntu) javaVersionElements[0] = javaVersionElements[0].substring(0, indexOfEarlyAccessSuffix); } } } final int major, minor; if (javaVersionElements.length >= 2) { major = Integer.parseInt(javaVersionElements[0]); int min; try { min = Integer.parseInt(javaVersionElements[1]); } catch (NumberFormatException nfe) { min = 7; } minor = min; } else { major = 1; int min; try { min = Integer.parseInt(javaVersionElements[0]); } catch (NumberFormatException nfe) { min = 7; } minor = min; } final String directBufferAttachmentFieldName; if (minor == 1 && major <= 6) directBufferAttachmentFieldName = java14to16DirectBufferAttachmentFieldName; else directBufferAttachmentFieldName = java17to19DirectBufferAttachmentFieldName; for (final String directBufferClassname : directBufferClassnames) attachmentOrByteBufferFieldNameMap.put(directBufferClassname, directBufferAttachmentFieldName); for (final String byteBufferAsNonByteBufferClassname : byteBufferAsNonByteBufferClassnames) attachmentOrByteBufferFieldNameMap.put(byteBufferAsNonByteBufferClassname, byteBufferAsNonByteBufferByteBufferFieldName); } else if (javaVendor.equals("The Android Project")) { final String byteBufferAsNonByteBufferByteBufferFieldName = "byteBuffer"; final String[] byteBufferAsNonByteBufferClassnames = new String[] { "java.nio.ByteBufferAsCharBuffer", "java.nio.ByteBufferAsDoubleBuffer", "java.nio.ByteBufferAsFloatBuffer", "java.nio.ByteBufferAsIntBuffer", "java.nio.ByteBufferAsLongBuffer", "java.nio.ByteBufferAsShortBuffer" }; for (final String byteBufferAsNonByteBufferClassname : byteBufferAsNonByteBufferClassnames) attachmentOrByteBufferFieldNameMap.put(byteBufferAsNonByteBufferClassname, byteBufferAsNonByteBufferByteBufferFieldName); } else if (/* javaVendor.equals("Apple Inc.")|| */javaVendor.equals("Free Software Foundation, Inc.")) { final String byteBufferAsNonByteBufferByteBufferFieldName = "bb"; final String[] byteBufferAsNonByteBufferClassnames = new String[] { "java.nio.CharViewBufferImpl", "java.nio.DoubleViewBufferImpl", "java.nio.FloatViewBufferImpl", "java.nio.IntViewBufferImpl", "java.nio.LongViewBufferImpl", "java.nio.ShortViewBufferImpl" }; for (final String byteBufferAsNonByteBufferClassname : byteBufferAsNonByteBufferClassnames) attachmentOrByteBufferFieldNameMap.put(byteBufferAsNonByteBufferClassname, byteBufferAsNonByteBufferByteBufferFieldName); } else if (javaVendor.contains("Apache")) { final String byteBufferAsNonByteBufferByteBufferFieldName = "byteBuffer"; final String[] byteBufferAsNonByteBufferClassnames = new String[] { "java.nio.CharToByteBufferAdapter", "java.nio.DoubleToByteBufferAdapter", "java.nio.FloatToByteBufferAdapter", "java.nio.IntToByteBufferAdapter", "java.nio.LongToByteBufferAdapter", "java.nio.ShortToByteBufferAdapter" }; for (final String byteBufferAsNonByteBufferClassname : byteBufferAsNonByteBufferClassnames) attachmentOrByteBufferFieldNameMap.put(byteBufferAsNonByteBufferClassname, byteBufferAsNonByteBufferByteBufferFieldName); } else if (javaVendor.equals("Jeroen Frijters")) {// TODO IKVM } else if (javaVendor.contains("IBM")) {// TODO J9 } } // checks if these classes are in the class library if (!attachmentOrByteBufferFieldNameMap.isEmpty()) { final List<String> classnamesToRemove = new ArrayList<>(); for (final String classname : attachmentOrByteBufferFieldNameMap.keySet()) try { Class.forName(classname); } catch (ClassNotFoundException cnfe) { classnamesToRemove.add(classname); } for (final String classnameToRemove : classnamesToRemove) attachmentOrByteBufferFieldNameMap.remove(classnameToRemove); } // builds the map used to determine the fields containing the direct // byte buffers attachmentOrByteBufferFieldMap = new HashMap<>(); if (!attachmentOrByteBufferFieldNameMap.isEmpty()) for (final Entry<String, String> attachmentOrByteBufferFieldNameEntry : attachmentOrByteBufferFieldNameMap .entrySet()) { final String classname = attachmentOrByteBufferFieldNameEntry.getKey(); final String fieldname = attachmentOrByteBufferFieldNameEntry.getValue(); try { final Class<?> bufferClass = Class.forName(classname); Field bufferField = null; Class<?> bufferIntermediaryClass = bufferClass; final List<Class<?>> intermediaryClassWithoutBufferList = new ArrayList<>(); while (bufferIntermediaryClass != null) { try { bufferField = bufferIntermediaryClass.getDeclaredField(fieldname); } catch (NoSuchFieldException nsfe) { if (!bufferIntermediaryClass.equals(Object.class) && !bufferIntermediaryClass.equals(Buffer.class)) intermediaryClassWithoutBufferList.add(bufferIntermediaryClass); } bufferIntermediaryClass = bufferIntermediaryClass.getSuperclass(); } if (bufferField == null) { final String superClassesMsg; if (intermediaryClassWithoutBufferList.isEmpty()) superClassesMsg = ""; else if (intermediaryClassWithoutBufferList.size() == 1) superClassesMsg = " and in its super class " + intermediaryClassWithoutBufferList.get(0).getName(); else { final StringBuilder builder = new StringBuilder(); builder.append(" and in its super classes"); int classIndex = 0; for (final Class<?> intermediaryClassWithoutBuffer : intermediaryClassWithoutBufferList) { builder.append(' '); builder.append(intermediaryClassWithoutBuffer.getName()); if (classIndex < intermediaryClassWithoutBufferList.size() - 1) builder.append(','); classIndex++; } superClassesMsg = builder.toString(); } logger.warn("The field " + fieldname + " hasn't been found in the class " + classname + superClassesMsg); } else {// the field has been found, stores it into the map attachmentOrByteBufferFieldMap.put(bufferClass, bufferField); } } catch (ClassNotFoundException cnfe) {// TODO The Java version // isn't very useful // under // Android as it is // always zero, rather // use // android.os.Build.VERSION.RELEASE // to show something // meaningful supported // since the API level 1 final String msg = "The class " + classname + " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor + " Java version: " + javaVersion; logger.warn(msg, cnfe); } } // if a known implementation has drastically changed or if the current // implementation is unknown if (attachmentOrByteBufferFieldNameMap.isEmpty()) {// detects everything // with the // reflection API // creates all // possible kinds of // direct NIO buffer // that can contain // buffers (sliced // buffers and views) final ByteBuffer slicedBigEndianReadOnlyDirectByteBuffer = ((ByteBuffer) ByteBuffer.allocateDirect(2) .order(ByteOrder.BIG_ENDIAN).put((byte) 0).put((byte) 0).position(1).limit(2)).slice() .asReadOnlyBuffer(); final ByteBuffer slicedBigEndianReadWriteDirectByteBuffer = ((ByteBuffer) ByteBuffer.allocateDirect(2) .order(ByteOrder.BIG_ENDIAN).put((byte) 0).put((byte) 0).position(1).limit(2)).slice(); final CharBuffer bigEndianReadOnlyDirectCharBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.BIG_ENDIAN).asReadOnlyBuffer().asCharBuffer(); final CharBuffer bigEndianReadWriteDirectCharBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.BIG_ENDIAN).asCharBuffer(); final DoubleBuffer bigEndianReadOnlyDirectDoubleBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.BIG_ENDIAN).asReadOnlyBuffer().asDoubleBuffer(); final DoubleBuffer bigEndianReadWriteDirectDoubleBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.BIG_ENDIAN).asDoubleBuffer(); final FloatBuffer bigEndianReadOnlyDirectFloatBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.BIG_ENDIAN).asReadOnlyBuffer().asFloatBuffer(); final FloatBuffer bigEndianReadWriteDirectFloatBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.BIG_ENDIAN).asFloatBuffer(); final IntBuffer bigEndianReadOnlyDirectIntBuffer = ByteBuffer.allocateDirect(1).order(ByteOrder.BIG_ENDIAN) .asReadOnlyBuffer().asIntBuffer(); final IntBuffer bigEndianReadWriteDirectIntBuffer = ByteBuffer.allocateDirect(1).order(ByteOrder.BIG_ENDIAN) .asIntBuffer(); final LongBuffer bigEndianReadOnlyDirectLongBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.BIG_ENDIAN).asReadOnlyBuffer().asLongBuffer(); final LongBuffer bigEndianReadWriteDirectLongBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.BIG_ENDIAN).asLongBuffer(); final ShortBuffer bigEndianReadOnlyDirectShortBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.BIG_ENDIAN).asReadOnlyBuffer().asShortBuffer(); final ShortBuffer bigEndianReadWriteDirectShortBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.BIG_ENDIAN).asShortBuffer(); final ByteBuffer slicedLittleEndianReadOnlyDirectByteBuffer = ((ByteBuffer) ByteBuffer.allocateDirect(2) .order(ByteOrder.LITTLE_ENDIAN).put((byte) 0).put((byte) 0).position(1).limit(2)).slice() .asReadOnlyBuffer(); final ByteBuffer slicedLittleEndianReadWriteDirectByteBuffer = ((ByteBuffer) ByteBuffer.allocateDirect(2) .order(ByteOrder.LITTLE_ENDIAN).put((byte) 0).put((byte) 0).position(1).limit(2)).slice(); final CharBuffer littleEndianReadOnlyDirectCharBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asCharBuffer(); final CharBuffer littleEndianReadWriteDirectCharBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.LITTLE_ENDIAN).asCharBuffer(); final DoubleBuffer littleEndianReadOnlyDirectDoubleBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asDoubleBuffer(); final DoubleBuffer littleEndianReadWriteDirectDoubleBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer(); final FloatBuffer littleEndianReadOnlyDirectFloatBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asFloatBuffer(); final FloatBuffer littleEndianReadWriteDirectFloatBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer(); final IntBuffer littleEndianReadOnlyDirectIntBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asIntBuffer(); final IntBuffer littleEndianReadWriteDirectIntBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.LITTLE_ENDIAN).asIntBuffer(); final LongBuffer littleEndianReadOnlyDirectLongBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asLongBuffer(); final LongBuffer littleEndianReadWriteDirectLongBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.LITTLE_ENDIAN).asLongBuffer(); final ShortBuffer littleEndianReadOnlyDirectShortBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asShortBuffer(); final ShortBuffer littleEndianReadWriteDirectShortBuffer = ByteBuffer.allocateDirect(1) .order(ByteOrder.LITTLE_ENDIAN).asShortBuffer(); final List<Buffer> buffers = new ArrayList<>(); buffers.add(slicedBigEndianReadOnlyDirectByteBuffer); buffers.add(slicedBigEndianReadWriteDirectByteBuffer); buffers.add(bigEndianReadOnlyDirectCharBuffer); buffers.add(bigEndianReadWriteDirectCharBuffer); buffers.add(bigEndianReadOnlyDirectDoubleBuffer); buffers.add(bigEndianReadWriteDirectDoubleBuffer); buffers.add(bigEndianReadOnlyDirectFloatBuffer); buffers.add(bigEndianReadWriteDirectFloatBuffer); buffers.add(bigEndianReadOnlyDirectIntBuffer); buffers.add(bigEndianReadWriteDirectIntBuffer); buffers.add(bigEndianReadOnlyDirectLongBuffer); buffers.add(bigEndianReadWriteDirectLongBuffer); buffers.add(bigEndianReadOnlyDirectShortBuffer); buffers.add(bigEndianReadWriteDirectShortBuffer); buffers.add(slicedLittleEndianReadOnlyDirectByteBuffer); buffers.add(slicedLittleEndianReadWriteDirectByteBuffer); buffers.add(littleEndianReadOnlyDirectCharBuffer); buffers.add(littleEndianReadWriteDirectCharBuffer); buffers.add(littleEndianReadOnlyDirectDoubleBuffer); buffers.add(littleEndianReadWriteDirectDoubleBuffer); buffers.add(littleEndianReadOnlyDirectFloatBuffer); buffers.add(littleEndianReadWriteDirectFloatBuffer); buffers.add(littleEndianReadOnlyDirectIntBuffer); buffers.add(littleEndianReadWriteDirectIntBuffer); buffers.add(littleEndianReadOnlyDirectLongBuffer); buffers.add(littleEndianReadWriteDirectLongBuffer); buffers.add(littleEndianReadOnlyDirectShortBuffer); buffers.add(littleEndianReadWriteDirectShortBuffer); // gets the fields to access the contained buffers for (Buffer buffer : buffers) { final Class<?> bufferClass = buffer.getClass(); if (!attachmentOrByteBufferFieldMap.containsKey(bufferClass)) { Field bufferField = null; Class<?> bufferIntermediaryClass = bufferClass; while (bufferIntermediaryClass != null && bufferField == null) { for (final Field field : bufferIntermediaryClass.getDeclaredFields()) { final boolean fieldWasAccessible = field.isAccessible(); try { field.setAccessible(true); final Object fieldValue = field.get(buffer); if (fieldValue != null && fieldValue instanceof Buffer) { bufferField = field; break; } } catch (IllegalAccessException iae) { logger.warn("Cannot access the field " + field.getName() + " of the class " + bufferIntermediaryClass.getName(), iae); } finally { field.setAccessible(fieldWasAccessible); } } bufferIntermediaryClass = bufferIntermediaryClass.getSuperclass(); } if (bufferField != null) attachmentOrByteBufferFieldMap.put(bufferClass, bufferField); } } // cleans the mess buffersToDelete.addAll(buffers); } // builds the set of classes whose instances can be deallocated deallocatableBufferClassSet = new HashSet<>(); if (javaVendor.equals("Sun Microsystems Inc.") || javaVendor.equals("Oracle Corporation") || javaVendor.equals("The Android Project")) { Class<?> directByteBufferClass = null; final String directByteBufferClassName = "java.nio.DirectByteBuffer"; try { directByteBufferClass = Class.forName(directByteBufferClassName); } catch (ClassNotFoundException cnfe) { final String msg = "The class " + directByteBufferClassName + " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor + " Java version: " + javaVersion; logger.warn(msg, cnfe); } if (directByteBufferClass != null) deallocatableBufferClassSet.add(directByteBufferClass); } else if (/* javaVendor.equals("Apple Inc.")|| */javaVendor.equals("Free Software Foundation, Inc.")) { Class<?> readOnlyDirectByteBufferClass = null; final String readOnlyDirectByteBufferClassName = "java.nio.DirectByteBufferImpl.ReadOnly"; try { readOnlyDirectByteBufferClass = Class.forName(readOnlyDirectByteBufferClassName); } catch (ClassNotFoundException cnfe) { final String msg = "The class " + readOnlyDirectByteBufferClassName + " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor + " Java version: " + javaVersion; logger.warn(msg, cnfe); } if (readOnlyDirectByteBufferClass != null) deallocatableBufferClassSet.add(readOnlyDirectByteBufferClass); Class<?> readWriteDirectByteBufferClass = null; final String readWriteDirectByteBufferClassName = "java.nio.DirectByteBufferImpl.ReadWrite"; try { readWriteDirectByteBufferClass = Class.forName(readWriteDirectByteBufferClassName); } catch (ClassNotFoundException cnfe) { final String msg = "The class " + readWriteDirectByteBufferClassName + " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor + " Java version: " + javaVersion; logger.warn(msg, cnfe); } if (readWriteDirectByteBufferClass != null) deallocatableBufferClassSet.add(readWriteDirectByteBufferClass); } else if (javaVendor.contains("Apache")) { Class<?> readOnlyDirectByteBufferClass = null; final String readOnlyDirectByteBufferClassName = "java.nio.ReadOnlyDirectByteBuffer"; try { readOnlyDirectByteBufferClass = Class.forName(readOnlyDirectByteBufferClassName); } catch (ClassNotFoundException cnfe) { final String msg = "The class " + readOnlyDirectByteBufferClassName + " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor + " Java version: " + javaVersion; logger.warn(msg, cnfe); } if (readOnlyDirectByteBufferClass != null) deallocatableBufferClassSet.add(readOnlyDirectByteBufferClass); Class<?> readWriteDirectByteBufferClass = null; final String readWriteDirectByteBufferClassName = "java.nio.ReadWriteDirectByteBuffer"; try { readWriteDirectByteBufferClass = Class.forName(readWriteDirectByteBufferClassName); } catch (ClassNotFoundException cnfe) { final String msg = "The class " + readWriteDirectByteBufferClassName + " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor + " Java version: " + javaVersion; logger.warn(msg, cnfe); } if (readWriteDirectByteBufferClass != null) deallocatableBufferClassSet.add(readWriteDirectByteBufferClass); } else if (javaVendor.equals("Jeroen Frijters")) {// TODO IKVM } else if (javaVendor.contains("IBM")) {// TODO J9 } // if there is no known implementation class of the direct byte buffers if (deallocatableBufferClassSet.isEmpty()) {// creates a read write // direct byte buffer final ByteBuffer dummyReadWriteDirectByteBuffer = ByteBuffer.allocateDirect(1); // gets its class final Class<?> readWriteDirectByteBufferClass = dummyReadWriteDirectByteBuffer.getClass(); // stores this class deallocatableBufferClassSet.add(readWriteDirectByteBufferClass); // cleans the mess buffersToDelete.add(dummyReadWriteDirectByteBuffer); // creates a read only direct byte buffer final ByteBuffer dummyReadOnlyDirectByteBuffer = ByteBuffer.allocateDirect(1).asReadOnlyBuffer(); // gets its class final Class<?> readOnlyDirectByteBufferClass = dummyReadOnlyDirectByteBuffer.getClass(); // stores this class deallocatableBufferClassSet.add(readOnlyDirectByteBufferClass); // cleans the mess buffersToDelete.add(dummyReadOnlyDirectByteBuffer); } // builds the deallocator responsible for releasing the native memory of // a deallocatable byte buffer if (javaVendor.equals("Sun Microsystems Inc.") || javaVendor.equals("Oracle Corporation")) deallocator = new OracleSunOpenJdkDeallocator(); else if (javaVendor.equals("The Android Project")) deallocator = new AndroidDeallocator(); else if (/* javaVendor.equals("Apple Inc.")|| */javaVendor.equals("Free Software Foundation, Inc.")) deallocator = new GnuClasspathDeallocator(); else if (javaVendor.contains("Apache")) deallocator = new ApacheHarmonyDeallocator(); else if (javaVendor.equals("Jeroen Frijters")) {// TODO IKVM deallocator = null; } else if (javaVendor.contains("IBM")) {// TODO J9 deallocator = null; } else deallocator = null; // final cleanup for (final Buffer bufferToDelete : buffersToDelete) deallocate(bufferToDelete); } public ByteBuffer findDeallocatableBuffer(Buffer buffer) { final ByteBuffer deallocatableDirectByteBuffer; // looks only for the direct buffers if (buffer != null && buffer.isDirect()) {// looks for any contained // buffer in the passed buffer final Class<?> bufferClass = buffer.getClass(); final Field attachmentOrByteBufferField = attachmentOrByteBufferFieldMap == null ? null : attachmentOrByteBufferFieldMap.get(bufferClass); final Buffer attachmentBufferOrByteBuffer; if (attachmentOrByteBufferField == null) attachmentBufferOrByteBuffer = null; else { Object attachedObjectOrByteBuffer; final boolean attachedObjectOrByteBufferFieldWasAccessible = attachmentOrByteBufferField.isAccessible(); try { attachmentOrByteBufferField.setAccessible(true); attachedObjectOrByteBuffer = attachmentOrByteBufferField.get(buffer); } catch (IllegalArgumentException | IllegalAccessException iae) { attachedObjectOrByteBuffer = null; } finally { attachmentOrByteBufferField.setAccessible(attachedObjectOrByteBufferFieldWasAccessible); } if (attachedObjectOrByteBuffer instanceof Buffer) attachmentBufferOrByteBuffer = (Buffer) attachedObjectOrByteBuffer; else attachmentBufferOrByteBuffer = null; } // if there is no buffer inside the buffer given in input if (attachmentBufferOrByteBuffer == null) {// if it's a direct byte // buffer and if it's an // instance of // a deallocatable buffer // class if (buffer instanceof ByteBuffer && deallocatableBufferClassSet.contains(bufferClass)) deallocatableDirectByteBuffer = (ByteBuffer) buffer; else {// it's not a byte buffer or it's not a // deallocatable buffer deallocatableDirectByteBuffer = null; final String bufferClassName = bufferClass.getName(); logger.warn("No deallocatable buffer has been found for an instance of the class " + bufferClassName + " whereas it is a direct NIO buffer"); } } else {// the passed buffer contains another buffer, looks for a // deallocatable buffer inside it deallocatableDirectByteBuffer = findDeallocatableBuffer(attachmentBufferOrByteBuffer); } } else {// there is no need to clean the heap based buffers deallocatableDirectByteBuffer = null; } return deallocatableDirectByteBuffer; } public void deallocate(final Buffer buffer) { if (deallocator != null) { final ByteBuffer deallocatableBuffer = findDeallocatableBuffer(buffer); if (deallocatableBuffer != null) deallocator.run(deallocatableBuffer); } } public Deallocator getDeallocator() { return (deallocator); } public void setDeallocator(Deallocator deallocator) { this.deallocator = deallocator; } public Map<Class<?>, Field> getAttachmentOrByteBufferFieldMap() { return (attachmentOrByteBufferFieldMap); } public void setAttachmentOrByteBufferFieldMap(Map<Class<?>, Field> attachmentOrByteBufferFieldMap) { this.attachmentOrByteBufferFieldMap = attachmentOrByteBufferFieldMap; } public Set<Class<?>> getDeallocatableBufferClassSet() { return (deallocatableBufferClassSet); } public void setDeallocatableBufferClassSet(Set<Class<?>> deallocatableBufferClassSet) { this.deallocatableBufferClassSet = deallocatableBufferClassSet; } }