Parcourir la source

BufferUtils : Apply changes made by Empire Phenix to properly track direct memory. This feature is defaulted to off.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9669 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
rem..om il y a 13 ans
Parent
commit
a04d5dde01
1 fichiers modifiés avec 249 ajouts et 195 suppressions
  1. 249 195
      engine/src/core/com/jme3/util/BufferUtils.java

+ 249 - 195
engine/src/core/com/jme3/util/BufferUtils.java

@@ -31,52 +31,55 @@
  */
 package com.jme3.util;
 
-import com.jme3.math.*;
+import java.lang.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.nio.*;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Map;
-import java.util.WeakHashMap;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.DoubleBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 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
  * jME data classes such as Vectors and ColorRGBA.
- *
+ * 
  * @author Joshua Slack
  * @version $Id: BufferUtils.java,v 1.16 2007/10/29 16:56:18 nca Exp $
  */
 public final class BufferUtils {
 
-    private static final Map<Buffer, Object> trackingHash = Collections.synchronizedMap(new WeakHashMap<Buffer, Object>());
-    private static final Object ref = new Object();
-    
-    // Note: a WeakHashMap is really bad here since the hashCode() and
-    //       equals() behavior of buffers will vary based on their contents.
-    //       As it stands, put()'ing an empty buffer will wipe out the last
-    //       empty buffer with the same size.  So any tracked memory calculations
-    //       could be lying.
-    //       Besides, the hashmap behavior isn't even being used here and
-    //       yet the expense is still incurred.  For example, a newly allocated
-    //       10,000 byte buffer will iterate through the whole buffer of 0's
-    //       to calculate the hashCode and then potentially do it again to
-    //       calculate the equals()... which by the way is guaranteed for
-    //       every empty buffer of an existing size since they will always produce 
-    //       the same hashCode().
-    //       It would be better to just keep a straight list of weak references
-    //       and clean out the dead every time a new buffer is allocated.
-    //       WeakHashMap is doing that anyway... so there is no extra expense 
-    //       incurred.
-    //       Recommend a ConcurrentLinkedQueue of WeakReferences since it
-    //       supports the threading semantics required with little extra overhead. 
-    private static final boolean trackDirectMemory = false;
+    private static boolean trackDirectMemory = false;
+    private static ReferenceQueue<Buffer> removeCollected = new ReferenceQueue<Buffer>();
+    private static ConcurrentHashMap<BufferInfo, BufferInfo> trackedBuffers = new ConcurrentHashMap<BufferInfo, BufferInfo>();
+    static ClearReferences cleanupthread;
 
     /**
-     * Creates a clone of the given buffer. The clone's capacity is
+     * 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) {
+        trackDirectMemory = enabled;
+    }
+ 
+    /**
+     * 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
@@ -97,63 +100,68 @@ public final class BufferUtils {
             throw new UnsupportedOperationException();
         }
     }
-    
-    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;
+
+    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) { } }
+         */
+        if (BufferUtils.trackDirectMemory) {
+
+            if (BufferUtils.cleanupthread == null) {
+                BufferUtils.cleanupthread = new ClearReferences();
+                BufferUtils.cleanupthread.start();
             }
-            
-            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) {
+            if (buffer instanceof ByteBuffer) {
+                BufferInfo info = new BufferInfo(ByteBuffer.class, buffer.capacity(), buffer, BufferUtils.removeCollected);
+                BufferUtils.trackedBuffers.put(info, info);
+            } else if (buffer instanceof FloatBuffer) {
+                BufferInfo info = new BufferInfo(FloatBuffer.class, buffer.capacity() * 4, buffer, BufferUtils.removeCollected);
+                BufferUtils.trackedBuffers.put(info, info);
+            } else if (buffer instanceof IntBuffer) {
+                BufferInfo info = new BufferInfo(IntBuffer.class, buffer.capacity() * 4, buffer, BufferUtils.removeCollected);
+                BufferUtils.trackedBuffers.put(info, info);
+            } else if (buffer instanceof ShortBuffer) {
+                BufferInfo info = new BufferInfo(ShortBuffer.class, buffer.capacity() * 2, buffer, BufferUtils.removeCollected);
+                BufferUtils.trackedBuffers.put(info, info);
+            } else if (buffer instanceof DoubleBuffer) {
+                BufferInfo info = new BufferInfo(DoubleBuffer.class, buffer.capacity() * 8, buffer, BufferUtils.removeCollected);
+                BufferUtils.trackedBuffers.put(info, info);
             }
-        }*/
-        
-        if (trackDirectMemory){
-            trackingHash.put(buffer, ref);
+
         }
     }
 
     /**
-     * Generate a new FloatBuffer using the given array of Vector3f objects.
-     * The FloatBuffer will be 3 * data.length long and contain the vector data
+     * 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
      */
     public static FloatBuffer createFloatBuffer(Vector3f... data) {
@@ -161,9 +169,9 @@ public final class BufferUtils {
             return null;
         }
         FloatBuffer buff = createFloatBuffer(3 * data.length);
-        for (int x = 0; x < data.length; x++) {
-            if (data[x] != null) {
-                buff.put(data[x].x).put(data[x].y).put(data[x].z);
+        for (Vector3f element : data) {
+            if (element != null) {
+                buff.put(element.x).put(element.y).put(element.z);
             } else {
                 buff.put(0).put(0).put(0);
             }
@@ -175,7 +183,7 @@ public final class BufferUtils {
     /**
      * Generate a new FloatBuffer using the given array of Quaternion objects.
      * 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
      */
     public static FloatBuffer createFloatBuffer(Quaternion... data) {
@@ -183,9 +191,9 @@ public final class BufferUtils {
             return null;
         }
         FloatBuffer buff = createFloatBuffer(4 * data.length);
-        for (int x = 0; x < data.length; x++) {
-            if (data[x] != null) {
-                buff.put(data[x].getX()).put(data[x].getY()).put(data[x].getZ()).put(data[x].getW());
+        for (Quaternion element : data) {
+            if (element != null) {
+                buff.put(element.getX()).put(element.getY()).put(element.getZ()).put(element.getW());
             } else {
                 buff.put(0).put(0).put(0);
             }
@@ -193,7 +201,7 @@ public final class BufferUtils {
         buff.flip();
         return buff;
     }
-    
+
     /**
      * Generate a new FloatBuffer using the given array of Vector4 objects.
      * The FloatBuffer will be 4 * data.length long and contain the vector data.
@@ -234,7 +242,7 @@ public final class BufferUtils {
     /**
      * Create a new FloatBuffer of an appropriate size to hold the specified
      * number of Vector3f object data.
-     *
+     * 
      * @param vertices
      *            number of vertices that need to be held by the newly created
      *            buffer
@@ -249,7 +257,7 @@ public final class BufferUtils {
      * Create a new FloatBuffer of an appropriate size to hold the specified
      * number of Vector3f object data only if the given buffer if not already
      * the right size.
-     *
+     * 
      * @param buf
      *            the buffer to first check and rewind
      * @param vertices
@@ -269,7 +277,7 @@ public final class BufferUtils {
     /**
      * Sets the data contained in the given color into the FloatBuffer at the
      * specified index.
-     *
+     * 
      * @param color
      *            the data to insert
      * @param buf
@@ -287,9 +295,9 @@ public final class BufferUtils {
     }
 
     /**
-     * Sets the data contained in the given quaternion into the FloatBuffer at the
+     * Sets the data contained in the given quaternion into the FloatBuffer at the 
      * specified index.
-     *
+     * 
      * @param quat
      *            the {@link Quaternion} to insert
      * @param buf
@@ -297,7 +305,7 @@ public final class BufferUtils {
      * @param index
      *            the postion to place the data; in terms of quaternions not floats
      */
-    public static void setInBuffer(Quaternion quat, FloatBuffer buf,
+    public static void setInBuffer(Quaternion quat, FloatBuffer buf, 
             int index) {
         buf.position(index * 4);
         buf.put(quat.getX());
@@ -329,7 +337,7 @@ public final class BufferUtils {
     /**
      * Sets the data contained in the given Vector3F into the FloatBuffer at the
      * specified index.
-     *
+     * 
      * @param vector
      *            the data to insert
      * @param buf
@@ -351,11 +359,11 @@ public final class BufferUtils {
             buf.put((index * 3) + 2, vector.z);
         }
     }
-
+    
     /**
      * Updates the values of the given vector from the specified buffer at the
      * index provided.
-     *
+     * 
      * @param vector
      *            the vector to set data on
      * @param buf
@@ -369,10 +377,10 @@ public final class BufferUtils {
         vector.y = buf.get(index * 3 + 1);
         vector.z = buf.get(index * 3 + 2);
     }
-
-    /**
+    
+     /**
      * Generates a Vector3f array from the given FloatBuffer.
-     *
+     * 
      * @param buff
      *            the FloatBuffer to read from
      * @return a newly generated array of Vector3f objects
@@ -391,7 +399,7 @@ public final class BufferUtils {
      * 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 postions 0-2
      * in the FloatBuffer.)
-     *
+     * 
      * @param buf
      *            the buffer to copy from/to
      * @param fromPos
@@ -405,7 +413,7 @@ public final class BufferUtils {
 
     /**
      * Normalize a Vector3f in-buffer.
-     *
+     * 
      * @param buf
      *            the buffer to find the Vector3f within
      * @param index
@@ -423,7 +431,7 @@ public final class BufferUtils {
 
     /**
      * Add to a Vector3f in-buffer.
-     *
+     * 
      * @param toAdd
      *            the vector to add from
      * @param buf
@@ -443,7 +451,7 @@ public final class BufferUtils {
 
     /**
      * Multiply and store a Vector3f in-buffer.
-     *
+     * 
      * @param toMult
      *            the vector to multiply against
      * @param buf
@@ -464,7 +472,7 @@ public final class BufferUtils {
     /**
      * Checks to see if the given Vector3f is equals to the data stored in the
      * buffer at the given data index.
-     *
+     * 
      * @param check
      *            the vector to check against - null will return false.
      * @param buf
@@ -488,7 +496,7 @@ public final class BufferUtils {
      * 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
      */
     public static FloatBuffer createFloatBuffer(Vector2f... data) {
@@ -496,9 +504,9 @@ public final class BufferUtils {
             return null;
         }
         FloatBuffer buff = createFloatBuffer(2 * data.length);
-        for (int x = 0; x < data.length; x++) {
-            if (data[x] != null) {
-                buff.put(data[x].x).put(data[x].y);
+        for (Vector2f element : data) {
+            if (element != null) {
+                buff.put(element.x).put(element.y);
             } else {
                 buff.put(0).put(0);
             }
@@ -510,7 +518,7 @@ public final class BufferUtils {
     /**
      * Create a new FloatBuffer of an appropriate size to hold the specified
      * number of Vector2f object data.
-     *
+     * 
      * @param vertices
      *            number of vertices that need to be held by the newly created
      *            buffer
@@ -525,7 +533,7 @@ public final class BufferUtils {
      * Create a new FloatBuffer of an appropriate size to hold the specified
      * number of Vector2f object data only if the given buffer if not already
      * the right size.
-     *
+     * 
      * @param buf
      *            the buffer to first check and rewind
      * @param vertices
@@ -545,7 +553,7 @@ public final class BufferUtils {
     /**
      * Sets the data contained in the given Vector2F into the FloatBuffer at the
      * specified index.
-     *
+     * 
      * @param vector
      *            the data to insert
      * @param buf
@@ -561,7 +569,7 @@ public final class BufferUtils {
     /**
      * Updates the values of the given vector from the specified buffer at the
      * index provided.
-     *
+     * 
      * @param vector
      *            the vector to set data on
      * @param buf
@@ -577,7 +585,7 @@ public final class BufferUtils {
 
     /**
      * Generates a Vector2f array from the given FloatBuffer.
-     *
+     * 
      * @param buff
      *            the FloatBuffer to read from
      * @return a newly generated array of Vector2f objects
@@ -596,7 +604,7 @@ public final class BufferUtils {
      * 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 postions 0-1
      * in the FloatBuffer.)
-     *
+     * 
      * @param buf
      *            the buffer to copy from/to
      * @param fromPos
@@ -610,7 +618,7 @@ public final class BufferUtils {
 
     /**
      * Normalize a Vector2f in-buffer.
-     *
+     * 
      * @param buf
      *            the buffer to find the Vector2f within
      * @param index
@@ -628,7 +636,7 @@ public final class BufferUtils {
 
     /**
      * Add to a Vector2f in-buffer.
-     *
+     * 
      * @param toAdd
      *            the vector to add from
      * @param buf
@@ -648,7 +656,7 @@ public final class BufferUtils {
 
     /**
      * Multiply and store a Vector2f in-buffer.
-     *
+     * 
      * @param toMult
      *            the vector to multiply against
      * @param buf
@@ -669,7 +677,7 @@ public final class BufferUtils {
     /**
      * Checks to see if the given Vector2f is equals to the data stored in the
      * buffer at the given data index.
-     *
+     * 
      * @param check
      *            the vector to check against - null will return false.
      * @param buf
@@ -693,7 +701,7 @@ public final class BufferUtils {
      * 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]...
      * etc.
-     *
+     * 
      * @param data
      *            array of ints to place into a new IntBuffer
      */
@@ -711,7 +719,7 @@ public final class BufferUtils {
     /**
      * Create a new int[] array and populate it with the given IntBuffer's
      * contents.
-     *
+     * 
      * @param buff
      *            the IntBuffer to read from
      * @return a new int array populated from the IntBuffer
@@ -731,7 +739,7 @@ public final class BufferUtils {
     /**
      * Create a new float[] array and populate it with the given FloatBuffer's
      * contents.
-     *
+     * 
      * @param buff
      *            the FloatBuffer to read from
      * @return a new float array populated from the FloatBuffer
@@ -751,7 +759,7 @@ public final class BufferUtils {
     //// -- GENERAL DOUBLE ROUTINES -- ////
     /**
      * Create a new DoubleBuffer of the specified size.
-     *
+     * 
      * @param size
      *            required number of double to store.
      * @return the new DoubleBuffer
@@ -766,7 +774,7 @@ public final class BufferUtils {
     /**
      * Create a new DoubleBuffer of an appropriate size to hold the specified
      * number of doubles only if the given buffer if not already the right size.
-     *
+     * 
      * @param buf
      *            the buffer to first check and rewind
      * @param size
@@ -789,7 +797,7 @@ public final class BufferUtils {
      * DoubleBuffer. The new DoubleBuffer is seperate from the old one and
      * changes are not reflected across. If you want to reflect changes,
      * consider using Buffer.duplicate().
-     *
+     * 
      * @param buf
      *            the DoubleBuffer to copy
      * @return the copy
@@ -814,7 +822,7 @@ public final class BufferUtils {
     //// -- GENERAL FLOAT ROUTINES -- ////
     /**
      * Create a new FloatBuffer of the specified size.
-     *
+     * 
      * @param size
      *            required number of floats to store.
      * @return the new FloatBuffer
@@ -828,7 +836,7 @@ public final class BufferUtils {
 
     /**
      * Copies floats from one position in the buffer to another.
-     *
+     * 
      * @param buf
      *            the buffer to copy from/to
      * @param fromPos
@@ -851,7 +859,7 @@ public final class BufferUtils {
      * FloatBuffer. The new FloatBuffer is seperate from the old one and changes
      * are not reflected across. If you want to reflect changes, consider using
      * Buffer.duplicate().
-     *
+     * 
      * @param buf
      *            the FloatBuffer to copy
      * @return the copy
@@ -876,7 +884,7 @@ public final class BufferUtils {
     //// -- GENERAL INT ROUTINES -- ////
     /**
      * Create a new IntBuffer of the specified size.
-     *
+     * 
      * @param size
      *            required number of ints to store.
      * @return the new IntBuffer
@@ -891,7 +899,7 @@ public final class BufferUtils {
     /**
      * Create a new IntBuffer of an appropriate size to hold the specified
      * number of ints only if the given buffer if not already the right size.
-     *
+     * 
      * @param buf
      *            the buffer to first check and rewind
      * @param size
@@ -914,7 +922,7 @@ public final class BufferUtils {
      * The new IntBuffer is seperate from the old one and changes are not
      * reflected across. If you want to reflect changes, consider using
      * Buffer.duplicate().
-     *
+     * 
      * @param buf
      *            the IntBuffer to copy
      * @return the copy
@@ -939,7 +947,7 @@ public final class BufferUtils {
     //// -- GENERAL BYTE ROUTINES -- ////
     /**
      * Create a new ByteBuffer of the specified size.
-     *
+     * 
      * @param size
      *            required number of ints to store.
      * @return the new IntBuffer
@@ -954,7 +962,7 @@ public final class BufferUtils {
     /**
      * Create a new ByteBuffer of an appropriate size to hold the specified
      * number of ints only if the given buffer if not already the right size.
-     *
+     * 
      * @param buf
      *            the buffer to first check and rewind
      * @param size
@@ -992,7 +1000,7 @@ public final class BufferUtils {
      * The new ByteBuffer is seperate from the old one and changes are not
      * reflected across. If you want to reflect changes, consider using
      * Buffer.duplicate().
-     *
+     * 
      * @param buf
      *            the ByteBuffer to copy
      * @return the copy
@@ -1017,7 +1025,7 @@ public final class BufferUtils {
     //// -- GENERAL SHORT ROUTINES -- ////
     /**
      * Create a new ShortBuffer of the specified size.
-     *
+     * 
      * @param size
      *            required number of shorts to store.
      * @return the new ShortBuffer
@@ -1032,7 +1040,7 @@ public final class BufferUtils {
     /**
      * Create a new ShortBuffer of an appropriate size to hold the specified
      * number of shorts only if the given buffer if not already the right size.
-     *
+     * 
      * @param buf
      *            the buffer to first check and rewind
      * @param size
@@ -1066,7 +1074,7 @@ public final class BufferUtils {
      * The new ShortBuffer is seperate from the old one and changes are not
      * reflected across. If you want to reflect changes, consider using
      * Buffer.duplicate().
-     *
+     * 
      * @param buf
      *            the ShortBuffer to copy
      * @return the copy
@@ -1140,55 +1148,59 @@ public final class BufferUtils {
 
     public static void printCurrentDirectMemory(StringBuilder store) {
         long totalHeld = 0;
-        // make a new set to hold the keys to prevent concurrency issues.
-        ArrayList<Buffer> bufs = new ArrayList<Buffer>(trackingHash.keySet());
-        int fBufs = 0, bBufs = 0, iBufs = 0, sBufs = 0, dBufs = 0;
-        int fBufsM = 0, bBufsM = 0, iBufsM = 0, sBufsM = 0, dBufsM = 0;
-        for (Buffer b : bufs) {
-            if (b instanceof ByteBuffer) {
-                totalHeld += b.capacity();
-                bBufsM += b.capacity();
-                bBufs++;
-            } else if (b instanceof FloatBuffer) {
-                totalHeld += b.capacity() * 4;
-                fBufsM += b.capacity() * 4;
-                fBufs++;
-            } else if (b instanceof IntBuffer) {
-                totalHeld += b.capacity() * 4;
-                iBufsM += b.capacity() * 4;
-                iBufs++;
-            } else if (b instanceof ShortBuffer) {
-                totalHeld += b.capacity() * 2;
-                sBufsM += b.capacity() * 2;
-                sBufs++;
-            } else if (b instanceof DoubleBuffer) {
-                totalHeld += b.capacity() * 8;
-                dBufsM += b.capacity() * 8;
-                dBufs++;
-            }
-        }
-        long heapMem = Runtime.getRuntime().totalMemory()
-                - Runtime.getRuntime().freeMemory();
-
+        long heapMem = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+        
         boolean printStout = store == null;
         if (store == null) {
             store = new StringBuilder();
         }
-        store.append("Existing buffers: ").append(bufs.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("Total   heap memory held: ").append(heapMem / 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");
+        if (trackDirectMemory) {
+            // make a new set to hold the keys to prevent concurrency issues.
+            int fBufs = 0, bBufs = 0, iBufs = 0, sBufs = 0, dBufs = 0;
+            int fBufsM = 0, bBufsM = 0, iBufsM = 0, sBufsM = 0, dBufsM = 0;
+            for (BufferInfo b : BufferUtils.trackedBuffers.values()) {
+                if (b.type == ByteBuffer.class) {
+                    totalHeld += b.size;
+                    bBufsM += b.size;
+                    bBufs++;
+                } else if (b.type == FloatBuffer.class) {
+                    totalHeld += b.size;
+                    fBufsM += b.size;
+                    fBufs++;
+                } else if (b.type == IntBuffer.class) {
+                    totalHeld += b.size;
+                    iBufsM += b.size;
+                    iBufs++;
+                } else if (b.type == ShortBuffer.class) {
+                    totalHeld += b.size;
+                    sBufsM += b.size;
+                    sBufs++;
+                } else if (b.type == DoubleBuffer.class) {
+                    totalHeld += b.size;
+                    dBufsM += b.size;
+                    dBufs++;
+                }
+            }
+
+            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("Total   heap memory held: ").append(heapMem / 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");
+        } else {
+            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");
+        }
         if (printStout) {
             System.out.println(store.toString());
         }
     }
-    
+    private static final AtomicBoolean loadedMethods = new AtomicBoolean(false);
     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);
@@ -1202,44 +1214,54 @@ public final class BufferUtils {
             return null; // the direct buffer implementation was not found
         }
     }
-    
-    static {
-        // Oracle JRE / OpenJDK
+
+    private static void loadCleanerMethods() {
+        // If its already true, exit, if not, set it to true.
+        if (BufferUtils.loadedMethods.getAndSet(true)) {
+            return;
+        }
+        // This could potentially be called many times if used from multiple
+        // threads
+        synchronized (BufferUtils.loadedMethods) {
+            // 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 (???)
+                // 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 {
+            }
+
+            // Apache Harmony
+            ByteBuffer bb = BufferUtils.createByteBuffer(1);
+            Class<?> clazz = bb.getClass();
+            try {
             freeMethod = clazz.getMethod("free");
-        } catch (NoSuchMethodException ex) {
-        } catch (SecurityException ex) {
+            } 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
+     * explicitly calls the Cleaner method of a direct buffer.
+     * 
+     * @param toBeDestroyed
     *          The direct buffer that will be "cleaned". Utilizes reflection.
-    *          
-    */
+     * 
+     */
     public static void destroyDirectBuffer(Buffer toBeDestroyed) {
         if (!toBeDestroyed.isDirect()) {
             return;
         }
-        
+
+        BufferUtils.loadCleanerMethods();
+
         try {
             if (freeMethod != null) {
                 freeMethod.invoke(toBeDestroyed);
@@ -1267,4 +1289,36 @@ public final class BufferUtils {
             Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
         }
     }
-}
+
+    private static class BufferInfo extends PhantomReference<Buffer> {
+
+        private Class type;
+        private int size;
+
+        public BufferInfo(Class type, int size, Buffer referent, ReferenceQueue<? super Buffer> q) {
+            super(referent, q);
+            this.type = type;
+            this.size = size;
+        }
+    }
+
+    private static class ClearReferences extends Thread {
+
+        ClearReferences() {
+            this.setDaemon(true);
+        }
+
+        @Override
+        public void run() {
+            try {
+                while (true) {
+                    Reference<? extends Buffer> toclean = BufferUtils.removeCollected.remove();
+                    BufferUtils.trackedBuffers.remove(toclean);
+                }
+
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+}