2
0
Эх сурвалжийг харах

Merge pull request #533 from empirephoenix/master

BufferAllocator Round 2
Kirill Vainer 9 жил өмнө
parent
commit
651753c4d3

+ 12 - 0
jme3-core/src/main/java/com/jme3/util/BufferAllocator.java

@@ -0,0 +1,12 @@
+package com.jme3.util;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+
+public interface BufferAllocator {
+
+    void destroyDirectBuffer(Buffer toBeDestroyed);
+
+    ByteBuffer allocate(int size);
+
+}

+ 124 - 178
jme3-core/src/main/java/com/jme3/util/BufferUtils.java

@@ -31,17 +31,10 @@
  */
  */
 package com.jme3.util;
 package com.jme3.util;
 
 
-import com.jme3.math.ColorRGBA;
-import com.jme3.math.Quaternion;
-import com.jme3.math.Vector2f;
-import com.jme3.math.Vector3f;
-import com.jme3.math.Vector4f;
 import java.io.UnsupportedEncodingException;
 import java.io.UnsupportedEncodingException;
 import java.lang.ref.PhantomReference;
 import java.lang.ref.PhantomReference;
 import java.lang.ref.Reference;
 import java.lang.ref.Reference;
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.ReferenceQueue;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.nio.Buffer;
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.ByteOrder;
@@ -51,8 +44,12 @@ import java.nio.IntBuffer;
 import java.nio.LongBuffer;
 import java.nio.LongBuffer;
 import java.nio.ShortBuffer;
 import java.nio.ShortBuffer;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
+
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.math.Vector4f;
 
 
 /**
 /**
  * <code>BufferUtils</code> is a helper class for generating nio buffers from
  * <code>BufferUtils</code> is a helper class for generating nio buffers from
@@ -62,27 +59,52 @@ import java.util.logging.Logger;
  * @version $Id: BufferUtils.java,v 1.16 2007/10/29 16:56:18 nca Exp $
  * @version $Id: BufferUtils.java,v 1.16 2007/10/29 16:56:18 nca Exp $
  */
  */
 public final class BufferUtils {
 public final class BufferUtils {
+    private static BufferAllocator allocator = new PrimitiveAllocator();
 
 
     private static boolean trackDirectMemory = false;
     private static boolean trackDirectMemory = false;
     private static ReferenceQueue<Buffer> removeCollected = new ReferenceQueue<Buffer>();
     private static ReferenceQueue<Buffer> removeCollected = new ReferenceQueue<Buffer>();
     private static ConcurrentHashMap<BufferInfo, BufferInfo> trackedBuffers = new ConcurrentHashMap<BufferInfo, BufferInfo>();
     private static ConcurrentHashMap<BufferInfo, BufferInfo> trackedBuffers = new ConcurrentHashMap<BufferInfo, BufferInfo>();
     static ClearReferences cleanupthread;
     static ClearReferences cleanupthread;
 
 
+    private static boolean used;
+
+    static {
+        try {
+            allocator = new ReflectionAllocator();
+        } catch (Throwable t) {
+            t.printStackTrace();
+            System.err.println("Error using ReflectionAllocator");
+        }
+    }
+
+    /**
+     * Warning! do only set this before JME is started!
+     */
+    public static void setAllocator(BufferAllocator allocator) {
+        if (used) {
+            throw new IllegalStateException(
+                    "An Buffer was already allocated, since it is quite likely that other dispose methods will create native dangling pointers or other fun things, this is forbidden to be changed at runtime");
+        }
+        BufferUtils.allocator = allocator;
+    }
+
     /**
     /**
-     * Set it to true if you want to enable direct memory tracking for debugging purpose.
-     * Default is false.
-     * To print direct memory usage use BufferUtils.printCurrentDirectMemory(StringBuilder store);
-     * @param enabled 
+     * Set it to true if you want to enable direct memory tracking for debugging
+     * purpose. Default is false. To print direct memory usage use
+     * BufferUtils.printCurrentDirectMemory(StringBuilder store);
+     * 
+     * @param enabled
      */
      */
     public static void setTrackDirectMemoryEnabled(boolean enabled) {
     public static void setTrackDirectMemoryEnabled(boolean enabled) {
         trackDirectMemory = enabled;
         trackDirectMemory = enabled;
     }
     }
 
 
     /**
     /**
-     * Creates a clone of the given buffer. The clone's capacity is 
-     * equal to the given buffer's limit.
+     * Creates a clone of the given buffer. The clone's capacity is equal to the
+     * given buffer's limit.
      * 
      * 
-     * @param buf The buffer to clone
+     * @param buf
+     *            The buffer to clone
      * @return The cloned buffer
      * @return The cloned buffer
      */
      */
     public static Buffer clone(Buffer buf) {
     public static Buffer clone(Buffer buf) {
@@ -102,55 +124,31 @@ public final class BufferUtils {
     }
     }
 
 
     private static void onBufferAllocated(Buffer buffer) {
     private static void onBufferAllocated(Buffer buffer) {
-        /**
-         * StackTraceElement[] stackTrace = new Throwable().getStackTrace(); int
-         * initialIndex = 0;
-         * 
-         * for (int i = 0; i < stackTrace.length; i++){ if
-         * (!stackTrace[i].getClassName().equals(BufferUtils.class.getName())){
-         * initialIndex = i; break; } }
-         * 
-         * int allocated = buffer.capacity(); int size = 0;
-         * 
-         * if (buffer instanceof FloatBuffer){ size = 4; }else if (buffer
-         * instanceof ShortBuffer){ size = 2; }else if (buffer instanceof
-         * ByteBuffer){ size = 1; }else if (buffer instanceof IntBuffer){ size =
-         * 4; }else if (buffer instanceof DoubleBuffer){ size = 8; }
-         * 
-         * allocated *= size;
-         * 
-         * for (int i = initialIndex; i < stackTrace.length; i++){
-         * StackTraceElement element = stackTrace[i]; if
-         * (element.getClassName().startsWith("java")){ break; }
-         * 
-         * try { Class clazz = Class.forName(element.getClassName()); if (i ==
-         * initialIndex){
-         * System.out.println(clazz.getSimpleName()+"."+element.getMethodName
-         * ()+"():" + element.getLineNumber() + " allocated " + allocated);
-         * }else{ System.out.println(" at " +
-         * clazz.getSimpleName()+"."+element.getMethodName()+"()"); } } catch
-         * (ClassNotFoundException ex) { } }
-         */
+        used = true;
         if (BufferUtils.trackDirectMemory) {
         if (BufferUtils.trackDirectMemory) {
-
             if (BufferUtils.cleanupthread == null) {
             if (BufferUtils.cleanupthread == null) {
                 BufferUtils.cleanupthread = new ClearReferences();
                 BufferUtils.cleanupthread = new ClearReferences();
                 BufferUtils.cleanupthread.start();
                 BufferUtils.cleanupthread.start();
             }
             }
             if (buffer instanceof ByteBuffer) {
             if (buffer instanceof ByteBuffer) {
-                BufferInfo info = new BufferInfo(ByteBuffer.class, buffer.capacity(), buffer, BufferUtils.removeCollected);
+                BufferInfo info = new BufferInfo(ByteBuffer.class, buffer.capacity(), buffer,
+                        BufferUtils.removeCollected);
                 BufferUtils.trackedBuffers.put(info, info);
                 BufferUtils.trackedBuffers.put(info, info);
             } else if (buffer instanceof FloatBuffer) {
             } else if (buffer instanceof FloatBuffer) {
-                BufferInfo info = new BufferInfo(FloatBuffer.class, buffer.capacity() * 4, buffer, BufferUtils.removeCollected);
+                BufferInfo info = new BufferInfo(FloatBuffer.class, buffer.capacity() * 4, buffer,
+                        BufferUtils.removeCollected);
                 BufferUtils.trackedBuffers.put(info, info);
                 BufferUtils.trackedBuffers.put(info, info);
             } else if (buffer instanceof IntBuffer) {
             } else if (buffer instanceof IntBuffer) {
-                BufferInfo info = new BufferInfo(IntBuffer.class, buffer.capacity() * 4, buffer, BufferUtils.removeCollected);
+                BufferInfo info = new BufferInfo(IntBuffer.class, buffer.capacity() * 4, buffer,
+                        BufferUtils.removeCollected);
                 BufferUtils.trackedBuffers.put(info, info);
                 BufferUtils.trackedBuffers.put(info, info);
             } else if (buffer instanceof ShortBuffer) {
             } else if (buffer instanceof ShortBuffer) {
-                BufferInfo info = new BufferInfo(ShortBuffer.class, buffer.capacity() * 2, buffer, BufferUtils.removeCollected);
+                BufferInfo info = new BufferInfo(ShortBuffer.class, buffer.capacity() * 2, buffer,
+                        BufferUtils.removeCollected);
                 BufferUtils.trackedBuffers.put(info, info);
                 BufferUtils.trackedBuffers.put(info, info);
             } else if (buffer instanceof DoubleBuffer) {
             } else if (buffer instanceof DoubleBuffer) {
-                BufferInfo info = new BufferInfo(DoubleBuffer.class, buffer.capacity() * 8, buffer, BufferUtils.removeCollected);
+                BufferInfo info = new BufferInfo(DoubleBuffer.class, buffer.capacity() * 8, buffer,
+                        BufferUtils.removeCollected);
                 BufferUtils.trackedBuffers.put(info, info);
                 BufferUtils.trackedBuffers.put(info, info);
             }
             }
 
 
@@ -158,11 +156,12 @@ public final class BufferUtils {
     }
     }
 
 
     /**
     /**
-     * Generate a new FloatBuffer using the given array of Vector3f objects. 
-     * The FloatBuffer will be 3 * data.length long and contain the vector data 
-     * as data[0].x, data[0].y, data[0].z, data[1].x... etc.
+     * Generate a new FloatBuffer using the given array of Vector3f objects. The
+     * FloatBuffer will be 3 * data.length long and contain the vector data as
+     * data[0].x, data[0].y, data[0].z, data[1].x... etc.
      * 
      * 
-     * @param data array of Vector3f objects to place into a new FloatBuffer
+     * @param data
+     *            array of Vector3f objects to place into a new FloatBuffer
      */
      */
     public static FloatBuffer createFloatBuffer(Vector3f... data) {
     public static FloatBuffer createFloatBuffer(Vector3f... data) {
         if (data == null) {
         if (data == null) {
@@ -184,7 +183,8 @@ public final class BufferUtils {
      * Generate a new FloatBuffer using the given array of Quaternion objects.
      * Generate a new FloatBuffer using the given array of Quaternion objects.
      * The FloatBuffer will be 4 * data.length long and contain the vector data.
      * The FloatBuffer will be 4 * data.length long and contain the vector data.
      * 
      * 
-     * @param data array of Quaternion objects to place into a new FloatBuffer
+     * @param data
+     *            array of Quaternion objects to place into a new FloatBuffer
      */
      */
     public static FloatBuffer createFloatBuffer(Quaternion... data) {
     public static FloatBuffer createFloatBuffer(Quaternion... data) {
         if (data == null) {
         if (data == null) {
@@ -203,10 +203,11 @@ public final class BufferUtils {
     }
     }
 
 
     /**
     /**
-     * Generate a new FloatBuffer using the given array of Vector4 objects.
-     * The FloatBuffer will be 4 * data.length long and contain the vector data.
+     * Generate a new FloatBuffer using the given array of Vector4 objects. The
+     * FloatBuffer will be 4 * data.length long and contain the vector data.
      *
      *
-     * @param data array of Vector4 objects to place into a new FloatBuffer
+     * @param data
+     *            array of Vector4 objects to place into a new FloatBuffer
      */
      */
     public static FloatBuffer createFloatBuffer(Vector4f... data) {
     public static FloatBuffer createFloatBuffer(Vector4f... data) {
         if (data == null) {
         if (data == null) {
@@ -228,7 +229,8 @@ public final class BufferUtils {
      * Generate a new FloatBuffer using the given array of ColorRGBA objects.
      * Generate a new FloatBuffer using the given array of ColorRGBA objects.
      * The FloatBuffer will be 4 * data.length long and contain the color data.
      * The FloatBuffer will be 4 * data.length long and contain the color data.
      *
      *
-     * @param data array of ColorRGBA objects to place into a new FloatBuffer
+     * @param data
+     *            array of ColorRGBA objects to place into a new FloatBuffer
      */
      */
     public static FloatBuffer createFloatBuffer(ColorRGBA... data) {
     public static FloatBuffer createFloatBuffer(ColorRGBA... data) {
         if (data == null) {
         if (data == null) {
@@ -248,7 +250,9 @@ public final class BufferUtils {
 
 
     /**
     /**
      * Generate a new FloatBuffer using the given array of float primitives.
      * Generate a new FloatBuffer using the given array of float primitives.
-     * @param data array of float primitives to place into a new FloatBuffer
+     * 
+     * @param data
+     *            array of float primitives to place into a new FloatBuffer
      */
      */
     public static FloatBuffer createFloatBuffer(float... data) {
     public static FloatBuffer createFloatBuffer(float... data) {
         if (data == null) {
         if (data == null) {
@@ -307,8 +311,7 @@ public final class BufferUtils {
      * @param index
      * @param index
      *            the postion to place the data; in terms of colors not floats
      *            the postion to place the data; in terms of colors not floats
      */
      */
-    public static void setInBuffer(ColorRGBA color, FloatBuffer buf,
-            int index) {
+    public static void setInBuffer(ColorRGBA color, FloatBuffer buf, int index) {
         buf.position(index * 4);
         buf.position(index * 4);
         buf.put(color.r);
         buf.put(color.r);
         buf.put(color.g);
         buf.put(color.g);
@@ -317,18 +320,18 @@ public final class BufferUtils {
     }
     }
 
 
     /**
     /**
-     * Sets the data contained in the given quaternion into the FloatBuffer at the 
-     * specified index.
+     * Sets the data contained in the given quaternion into the FloatBuffer at
+     * the specified index.
      * 
      * 
      * @param quat
      * @param quat
      *            the {@link Quaternion} to insert
      *            the {@link Quaternion} to insert
      * @param buf
      * @param buf
      *            the buffer to insert into
      *            the buffer to insert into
      * @param index
      * @param index
-     *            the position to place the data; in terms of quaternions not floats
+     *            the position to place the data; in terms of quaternions not
+     *            floats
      */
      */
-    public static void setInBuffer(Quaternion quat, FloatBuffer buf,
-            int index) {
+    public static void setInBuffer(Quaternion quat, FloatBuffer buf, int index) {
         buf.position(index * 4);
         buf.position(index * 4);
         buf.put(quat.getX());
         buf.put(quat.getX());
         buf.put(quat.getY());
         buf.put(quat.getY());
@@ -347,8 +350,7 @@ public final class BufferUtils {
      * @param index
      * @param index
      *            the position to place the data; in terms of vector4 not floats
      *            the position to place the data; in terms of vector4 not floats
      */
      */
-    public static void setInBuffer(Vector4f vec, FloatBuffer buf,
-            int index) {
+    public static void setInBuffer(Vector4f vec, FloatBuffer buf, int index) {
         buf.position(index * 4);
         buf.position(index * 4);
         buf.put(vec.getX());
         buf.put(vec.getX());
         buf.put(vec.getY());
         buf.put(vec.getY());
@@ -438,8 +440,8 @@ public final class BufferUtils {
 
 
     /**
     /**
      * Copies a Vector3f from one position in the buffer to another. The index
      * Copies a Vector3f from one position in the buffer to another. The index
-     * values are in terms of vector number (eg, vector number 0 is positions 0-2
-     * in the FloatBuffer.)
+     * values are in terms of vector number (eg, vector number 0 is positions
+     * 0-2 in the FloatBuffer.)
      * 
      * 
      * @param buf
      * @param buf
      *            the buffer to copy from/to
      *            the buffer to copy from/to
@@ -534,11 +536,12 @@ public final class BufferUtils {
 
 
     // // -- VECTOR2F METHODS -- ////
     // // -- VECTOR2F METHODS -- ////
     /**
     /**
-     * Generate a new FloatBuffer using the given array of Vector2f objects.
-     * The FloatBuffer will be 2 * data.length long and contain the vector data
-     * as data[0].x, data[0].y, data[1].x... etc.
+     * Generate a new FloatBuffer using the given array of Vector2f objects. The
+     * FloatBuffer will be 2 * data.length long and contain the vector data as
+     * data[0].x, data[0].y, data[1].x... etc.
      * 
      * 
-     * @param data array of Vector2f objects to place into a new FloatBuffer
+     * @param data
+     *            array of Vector2f objects to place into a new FloatBuffer
      */
      */
     public static FloatBuffer createFloatBuffer(Vector2f... data) {
     public static FloatBuffer createFloatBuffer(Vector2f... data) {
         if (data == null) {
         if (data == null) {
@@ -643,8 +646,8 @@ public final class BufferUtils {
 
 
     /**
     /**
      * Copies a Vector2f from one position in the buffer to another. The index
      * Copies a Vector2f from one position in the buffer to another. The index
-     * values are in terms of vector number (eg, vector number 0 is positions 0-1
-     * in the FloatBuffer.)
+     * values are in terms of vector number (eg, vector number 0 is positions
+     * 0-1 in the FloatBuffer.)
      * 
      * 
      * @param buf
      * @param buf
      *            the buffer to copy from/to
      *            the buffer to copy from/to
@@ -737,7 +740,7 @@ public final class BufferUtils {
         return eq;
         return eq;
     }
     }
 
 
-    ////  -- INT METHODS -- ////
+    //// -- INT METHODS -- ////
     /**
     /**
      * Generate a new IntBuffer using the given array of ints. The IntBuffer
      * Generate a new IntBuffer using the given array of ints. The IntBuffer
      * will be data.length long and contain the int data as data[0], data[1]...
      * will be data.length long and contain the int data as data[0], data[1]...
@@ -806,7 +809,7 @@ public final class BufferUtils {
      * @return the new DoubleBuffer
      * @return the new DoubleBuffer
      */
      */
     public static DoubleBuffer createDoubleBuffer(int size) {
     public static DoubleBuffer createDoubleBuffer(int size) {
-        DoubleBuffer buf = ByteBuffer.allocateDirect(8 * size).order(ByteOrder.nativeOrder()).asDoubleBuffer();
+        DoubleBuffer buf = allocator.allocate(8 * size).order(ByteOrder.nativeOrder()).asDoubleBuffer();
         buf.clear();
         buf.clear();
         onBufferAllocated(buf);
         onBufferAllocated(buf);
         return buf;
         return buf;
@@ -869,7 +872,7 @@ public final class BufferUtils {
      * @return the new FloatBuffer
      * @return the new FloatBuffer
      */
      */
     public static FloatBuffer createFloatBuffer(int size) {
     public static FloatBuffer createFloatBuffer(int size) {
-        FloatBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asFloatBuffer();
+        FloatBuffer buf = allocator.allocate(4 * size).order(ByteOrder.nativeOrder()).asFloatBuffer();
         buf.clear();
         buf.clear();
         onBufferAllocated(buf);
         onBufferAllocated(buf);
         return buf;
         return buf;
@@ -931,7 +934,7 @@ public final class BufferUtils {
      * @return the new IntBuffer
      * @return the new IntBuffer
      */
      */
     public static IntBuffer createIntBuffer(int size) {
     public static IntBuffer createIntBuffer(int size) {
-        IntBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asIntBuffer();
+        IntBuffer buf = allocator.allocate(4 * size).order(ByteOrder.nativeOrder()).asIntBuffer();
         buf.clear();
         buf.clear();
         onBufferAllocated(buf);
         onBufferAllocated(buf);
         return buf;
         return buf;
@@ -994,7 +997,7 @@ public final class BufferUtils {
      * @return the new IntBuffer
      * @return the new IntBuffer
      */
      */
     public static ByteBuffer createByteBuffer(int size) {
     public static ByteBuffer createByteBuffer(int size) {
-        ByteBuffer buf = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
+        ByteBuffer buf = allocator.allocate(size).order(ByteOrder.nativeOrder());
         buf.clear();
         buf.clear();
         onBufferAllocated(buf);
         onBufferAllocated(buf);
         return buf;
         return buf;
@@ -1076,7 +1079,7 @@ public final class BufferUtils {
      * @return the new ShortBuffer
      * @return the new ShortBuffer
      */
      */
     public static ShortBuffer createShortBuffer(int size) {
     public static ShortBuffer createShortBuffer(int size) {
-        ShortBuffer buf = ByteBuffer.allocateDirect(2 * size).order(ByteOrder.nativeOrder()).asShortBuffer();
+        ShortBuffer buf = allocator.allocate(2 * size).order(ByteOrder.nativeOrder()).asShortBuffer();
         buf.clear();
         buf.clear();
         onBufferAllocated(buf);
         onBufferAllocated(buf);
         return buf;
         return buf;
@@ -1115,9 +1118,9 @@ public final class BufferUtils {
     }
     }
 
 
     /**
     /**
-     * Creates a new ShortBuffer with the same contents as the given ShortBuffer.
-     * The new ShortBuffer is separate from the old one and changes are not
-     * reflected across. If you want to reflect changes, consider using
+     * Creates a new ShortBuffer with the same contents as the given
+     * ShortBuffer. The new ShortBuffer is separate from the old one and changes
+     * are not reflected across. If you want to reflect changes, consider using
      * Buffer.duplicate().
      * Buffer.duplicate().
      * 
      * 
      * @param buf
      * @param buf
@@ -1142,12 +1145,18 @@ public final class BufferUtils {
     }
     }
 
 
     /**
     /**
-     * Ensures there is at least the <code>required</code> number of entries left after the current position of the
-     * buffer. If the buffer is too small a larger one is created and the old one copied to the new buffer.
-     * @param buffer buffer that should be checked/copied (may be null)
-     * @param required minimum number of elements that should be remaining in the returned buffer
-     * @return a buffer large enough to receive at least the <code>required</code> number of entries, same position as
-     * the input buffer, not null
+     * Ensures there is at least the <code>required</code> number of entries
+     * left after the current position of the buffer. If the buffer is too small
+     * a larger one is created and the old one copied to the new buffer.
+     * 
+     * @param buffer
+     *            buffer that should be checked/copied (may be null)
+     * @param required
+     *            minimum number of elements that should be remaining in the
+     *            returned buffer
+     * @return a buffer large enough to receive at least the
+     *         <code>required</code> number of entries, same position as the
+     *         input buffer, not null
      */
      */
     public static FloatBuffer ensureLargeEnough(FloatBuffer buffer, int required) {
     public static FloatBuffer ensureLargeEnough(FloatBuffer buffer, int required) {
         if (buffer != null) {
         if (buffer != null) {
@@ -1165,7 +1174,7 @@ public final class BufferUtils {
         }
         }
         return buffer;
         return buffer;
     }
     }
-    
+
     public static IntBuffer ensureLargeEnough(IntBuffer buffer, int required) {
     public static IntBuffer ensureLargeEnough(IntBuffer buffer, int required) {
         if (buffer != null) {
         if (buffer != null) {
             buffer.limit(buffer.capacity());
             buffer.limit(buffer.capacity());
@@ -1182,7 +1191,6 @@ public final class BufferUtils {
         }
         }
         return buffer;
         return buffer;
     }
     }
-    
 
 
     public static ShortBuffer ensureLargeEnough(ShortBuffer buffer, int required) {
     public static ShortBuffer ensureLargeEnough(ShortBuffer buffer, int required) {
         if (buffer != null) {
         if (buffer != null) {
@@ -1255,108 +1263,46 @@ public final class BufferUtils {
             }
             }
 
 
             store.append("Existing buffers: ").append(BufferUtils.trackedBuffers.size()).append("\n");
             store.append("Existing buffers: ").append(BufferUtils.trackedBuffers.size()).append("\n");
-            store.append("(b: ").append(bBufs).append("  f: ").append(fBufs).append("  i: ").append(iBufs).append("  s: ").append(sBufs).append("  d: ").append(dBufs).append(")").append("\n");
+            store.append("(b: ").append(bBufs).append("  f: ").append(fBufs).append("  i: ").append(iBufs)
+                    .append("  s: ").append(sBufs).append("  d: ").append(dBufs).append(")").append("\n");
             store.append("Total   heap memory held: ").append(heapMem / 1024).append("kb\n");
             store.append("Total   heap memory held: ").append(heapMem / 1024).append("kb\n");
             store.append("Total direct memory held: ").append(totalHeld / 1024).append("kb\n");
             store.append("Total direct memory held: ").append(totalHeld / 1024).append("kb\n");
-            store.append("(b: ").append(bBufsM / 1024).append("kb  f: ").append(fBufsM / 1024).append("kb  i: ").append(iBufsM / 1024).append("kb  s: ").append(sBufsM / 1024).append("kb  d: ").append(dBufsM / 1024).append("kb)").append("\n");
+            store.append("(b: ").append(bBufsM / 1024).append("kb  f: ").append(fBufsM / 1024).append("kb  i: ")
+                    .append(iBufsM / 1024).append("kb  s: ").append(sBufsM / 1024).append("kb  d: ")
+                    .append(dBufsM / 1024).append("kb)").append("\n");
         } else {
         } else {
             store.append("Total   heap memory held: ").append(heapMem / 1024).append("kb\n");
             store.append("Total   heap memory held: ").append(heapMem / 1024).append("kb\n");
-            store.append("Only heap memory available, if you want to monitor direct memory use BufferUtils.setTrackDirectMemoryEnabled(true) during initialization.").append("\n");
+            store.append(
+                    "Only heap memory available, if you want to monitor direct memory use BufferUtils.setTrackDirectMemoryEnabled(true) during initialization.")
+                    .append("\n");
         }
         }
         if (printStout) {
         if (printStout) {
             System.out.println(store.toString());
             System.out.println(store.toString());
         }
         }
     }
     }
-    private static Method cleanerMethod = null;
-    private static Method cleanMethod = null;
-    private static Method viewedBufferMethod = null;
-    private static Method freeMethod = null;
-
-    private static Method loadMethod(String className, String methodName) {
-        try {
-            Method method = Class.forName(className).getMethod(methodName);
-            method.setAccessible(true);
-            return method;
-        } catch (NoSuchMethodException ex) {
-            return null; // the method was not found
-        } catch (SecurityException ex) {
-            return null; // setAccessible not allowed by security policy
-        } catch (ClassNotFoundException ex) {
-            return null; // the direct buffer implementation was not found
-        }
-    }
-
-    static {
-        // Oracle JRE / OpenJDK
-        cleanerMethod = loadMethod("sun.nio.ch.DirectBuffer", "cleaner");
-        cleanMethod = loadMethod("sun.misc.Cleaner", "clean");
-        viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "viewedBuffer");
-        if (viewedBufferMethod == null) {
-            // They changed the name in Java 7 (???)
-            viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "attachment");
-        }
-
-        // Apache Harmony
-        ByteBuffer bb = BufferUtils.createByteBuffer(1);
-        Class<?> clazz = bb.getClass();
-        try {
-            freeMethod = clazz.getMethod("free");
-        } catch (NoSuchMethodException ex) {
-        } catch (SecurityException ex) {
-        }
-    }
 
 
     /**
     /**
      * Direct buffers are garbage collected by using a phantom reference and a
      * Direct buffers are garbage collected by using a phantom reference and a
-     * reference queue. Every once a while, the JVM checks the reference queue and
-     * cleans the direct buffers. However, as this doesn't happen
-     * immediately after discarding all references to a direct buffer, it's
-     * easy to OutOfMemoryError yourself using direct buffers. This function
-     * explicitly calls the Cleaner method of a direct buffer.
-     * 
-     * @param toBeDestroyed
-     *          The direct buffer that will be "cleaned". Utilizes reflection.
-     * 
-     */
+     * reference queue. Every once a while, the JVM checks the reference queue
+     * and cleans the direct buffers. However, as this doesn't happen
+     * immediately after discarding all references to a direct buffer, it's easy
+     * to OutOfMemoryError yourself using direct buffers.
+     **/
     public static void destroyDirectBuffer(Buffer toBeDestroyed) {
     public static void destroyDirectBuffer(Buffer toBeDestroyed) {
         if (!isDirect(toBeDestroyed)) {
         if (!isDirect(toBeDestroyed)) {
             return;
             return;
         }
         }
-
-        try {
-            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);
-                    }
-                }
-            }
-        } catch (IllegalAccessException ex) {
-            Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
-        } catch (IllegalArgumentException ex) {
-            Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
-        } catch (InvocationTargetException ex) {
-            Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
-        } catch (SecurityException ex) {
-            Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
-        }
+        allocator.destroyDirectBuffer(toBeDestroyed);
     }
     }
-    
+
     /*
     /*
-     * FIXME when java 1.5 supprt is dropped - replace calls to this method with Buffer.isDirect 
+     * FIXME when java 1.5 supprt is dropped - replace calls to this method with
+     * Buffer.isDirect
      * 
      * 
-     * Buffer.isDirect() is only java 6. Java 5 only have this method on Buffer subclasses : 
-     * FloatBuffer, IntBuffer, ShortBuffer, ByteBuffer,DoubleBuffer, LongBuffer.   
-     * CharBuffer has been excluded as we don't use it.
+     * Buffer.isDirect() is only java 6. Java 5 only have this method on Buffer
+     * subclasses : FloatBuffer, IntBuffer, ShortBuffer,
+     * ByteBuffer,DoubleBuffer, LongBuffer. CharBuffer has been excluded as we
+     * don't use it.
      * 
      * 
      */
      */
     private static boolean isDirect(Buffer buf) {
     private static boolean isDirect(Buffer buf) {

+ 55 - 0
jme3-core/src/main/java/com/jme3/util/PrimitiveAllocator.java

@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.util;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+
+/**
+ * This class contains a primitve allocator with no special logic, should work
+ * on any jvm
+ */
+public final class PrimitiveAllocator implements BufferAllocator {
+
+    @Override
+    public void destroyDirectBuffer(Buffer toBeDestroyed) {
+        // no exception by intent, as this way naivly written java7/8
+        // applications wont crash on 9 assuming they can dispose buffers
+        System.err.println("Warning destroyBuffer not supported");
+    }
+
+    @Override
+    public ByteBuffer allocate(int size) {
+        return ByteBuffer.allocateDirect(size);
+    }
+
+}

+ 130 - 0
jme3-core/src/main/java/com/jme3/util/ReflectionAllocator.java

@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class contains the reflection based way to remove DirectByteBuffers in
+ * java < 9, allocation is done via ByteBuffer.allocateDirect
+ */
+public final class ReflectionAllocator implements BufferAllocator {
+    private static Method cleanerMethod = null;
+    private static Method cleanMethod = null;
+    private static Method viewedBufferMethod = null;
+    private static Method freeMethod = null;
+
+    static {
+        // Oracle JRE / OpenJDK
+        cleanerMethod = loadMethod("sun.nio.ch.DirectBuffer", "cleaner");
+        cleanMethod = loadMethod("sun.misc.Cleaner", "clean");
+        viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "viewedBuffer");
+        if (viewedBufferMethod == null) {
+            // They changed the name in Java 7 (???)
+            viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "attachment");
+        }
+
+        // Apache Harmony (allocated directly, to not trigger allocator used
+        // logic in BufferUtils)
+        ByteBuffer bb = ByteBuffer.allocateDirect(1);
+        Class<?> clazz = bb.getClass();
+        try {
+            freeMethod = clazz.getMethod("free");
+        } catch (NoSuchMethodException ex) {
+        } catch (SecurityException ex) {
+        }
+    }
+
+    private static Method loadMethod(String className, String methodName) {
+        try {
+            Method method = Class.forName(className).getMethod(methodName);
+            method.setAccessible(true);
+            return method;
+        } catch (NoSuchMethodException ex) {
+            return null; // the method was not found
+        } catch (SecurityException ex) {
+            return null; // setAccessible not allowed by security policy
+        } catch (ClassNotFoundException ex) {
+            return null; // the direct buffer implementation was not found
+        }
+    }
+
+    @Override
+    /**
+     * This function explicitly calls the Cleaner method of a direct buffer.
+     * 
+     * @param toBeDestroyed
+     *            The direct buffer that will be "cleaned". Utilizes reflection.
+     * 
+     */
+    public void destroyDirectBuffer(Buffer toBeDestroyed) {
+        try {
+            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);
+                    }
+                }
+            }
+        } catch (IllegalAccessException ex) {
+            Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
+        } catch (IllegalArgumentException ex) {
+            Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
+        } catch (InvocationTargetException ex) {
+            Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
+        } catch (SecurityException ex) {
+            Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
+        }
+    }
+
+    @Override
+    public ByteBuffer allocate(int size) {
+        return ByteBuffer.allocateDirect(size);
+    }
+
+}