Small fix to JME

Creating a directbuffer without the use of Bufferutils, so it cannot be tracked.

Code:
Index: src/terrain/com/jme3/terrain/noise/ShaderUtils.java =================================================================== --- src/terrain/com/jme3/terrain/noise/ShaderUtils.java (revision 9113) +++ src/terrain/com/jme3/terrain/noise/ShaderUtils.java (working copy) @@ -38,9 +38,11 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder;

+import com.jme3.util.BufferUtils;
+
/**

    • Helper class containing useful functions explained in the book:
    • Texturing & Modeling - A Procedural Approach
    • Helper class containing useful functions explained in the book: Texturing &
    • Modeling - A Procedural Approach
    • @author Anthyon

@@ -48,13 +50,11 @@
public class ShaderUtils {

public static final float[] i2c(final int color) {
  •   return new float[] { (color & 0x00ff0000) / 256f, (color & 0x0000ff00) / 256f, (color & 0x000000ff) / 256f,
    
  •   		(color & 0xff000000) / 256f };
    
  •   return new float[] { (color & 0x00ff0000) / 256f, (color & 0x0000ff00) / 256f, (color & 0x000000ff) / 256f, (color & 0xff000000) / 256f };
    

    }

    public static final int c2i(final float[] color) {

  •   return (color.length == 4 ? (int) (color[3] * 256) : 0xff000000) | ((int) (color[0] * 256) << 16) | ((int) (color[1] * 256) << 8)
    
  •   		| (int) (color[2] * 256);
    
  •   return (color.length == 4 ? (int) (color[3] * 256) : 0xff000000) | ((int) (color[0] * 256) << 16) | ((int) (color[1] * 256) << 8) | (int) (color[2] * 256);
    

    }

    public static final float mix(final float a, final float b, final float f) {
    @@ -62,9 +62,7 @@
    }

    public static final Color mix(final Color a, final Color b, final float f) {

  •   return new Color((int) ShaderUtils.clamp(ShaderUtils.mix(a.getRed(), b.getRed(), f), 0, 255), (int) ShaderUtils.clamp(
    
  •   		ShaderUtils.mix(a.getGreen(), b.getGreen(), f), 0, 255), (int) ShaderUtils.clamp(
    
  •   		ShaderUtils.mix(a.getBlue(), b.getBlue(), f), 0, 255));
    
  •   return new Color((int) ShaderUtils.clamp(ShaderUtils.mix(a.getRed(), b.getRed(), f), 0, 255), (int) ShaderUtils.clamp(ShaderUtils.mix(a.getGreen(), b.getGreen(), f), 0, 255), (int) ShaderUtils.clamp(ShaderUtils.mix(a.getBlue(), b.getBlue(), f), 0, 255));
    

    }

    public static final int mix(final int a, final int b, final float f) {
    @@ -109,12 +107,12 @@
    } else if (x > b) {
    return 1;
    }

  •   float xx = (x - a) / (b - a);
    
  •   final float xx = (x - a) / (b - a);
      return xx * xx * (3 - 2 * xx);
    

    }

    public static final float mod(final float a, final float b) {

  •   int n = (int) (a / b);
    
  •   final int n = (int) (a / b);
      float aa = a - n * b;
      if (aa < 0) {
      	aa += b;
    

@@ -131,25 +129,25 @@
}

public static final float spline(float x, final float[] knot) {
  •   float CR00 = -0.5f;
    
  •   float CR01 = 1.5f;
    
  •   float CR02 = -1.5f;
    
  •   float CR03 = 0.5f;
    
  •   float CR10 = 1.0f;
    
  •   float CR11 = -2.5f;
    
  •   float CR12 = 2.0f;
    
  •   float CR13 = -0.5f;
    
  •   float CR20 = -0.5f;
    
  •   float CR21 = 0.0f;
    
  •   float CR22 = 0.5f;
    
  •   float CR23 = 0.0f;
    
  •   float CR30 = 0.0f;
    
  •   float CR31 = 1.0f;
    
  •   float CR32 = 0.0f;
    
  •   float CR33 = 0.0f;
    
  •   final float CR00 = -0.5f;
    
  •   final float CR01 = 1.5f;
    
  •   final float CR02 = -1.5f;
    
  •   final float CR03 = 0.5f;
    
  •   final float CR10 = 1.0f;
    
  •   final float CR11 = -2.5f;
    
  •   final float CR12 = 2.0f;
    
  •   final float CR13 = -0.5f;
    
  •   final float CR20 = -0.5f;
    
  •   final float CR21 = 0.0f;
    
  •   final float CR22 = 0.5f;
    
  •   final float CR23 = 0.0f;
    
  •   final float CR30 = 0.0f;
    
  •   final float CR31 = 1.0f;
    
  •   final float CR32 = 0.0f;
    
  •   final float CR33 = 0.0f;
    
      int span;
    
  •   int nspans = knot.length - 3;
    
  •   final int nspans = knot.length - 3;
      float c0, c1, c2, c3; /* coefficients of the cubic. */
      if (nspans < 1) {/* illegal */
      	throw new RuntimeException("Spline has too few knots.");
    

@@ -170,7 +168,7 @@
}

public static final float[] spline(final float x, final float[][] knots) {
  •   float[] retval = new float[knots.length];
    
  •   final float[] retval = new float[knots.length];
      for (int i = 0; i < knots.length; i++) {
      	retval[i] = ShaderUtils.spline(x, knots[i]);
      }
    

@@ -191,12 +189,12 @@

public static final float sinValue(final float s, final float minFreq, final float maxFreq, final float swidth) {
	float value = 0;
  •   float cutoff = ShaderUtils.clamp(0.5f / swidth, 0, maxFreq);
    
  •   final float cutoff = ShaderUtils.clamp(0.5f / swidth, 0, maxFreq);
      float f;
      for (f = minFreq; f < 0.5 * cutoff; f *= 2) {
      	value += Math.sin(2 * Math.PI * f * s) / f;
      }
    
  •   float fade = ShaderUtils.clamp(2 * (cutoff - f) / cutoff, 0, 1);
    
  •   final float fade = ShaderUtils.clamp(2 * (cutoff - f) / cutoff, 0, 1);
      value += fade * Math.sin(2 * Math.PI * f * s) / f;
      return value;
    

    }
    @@ -206,20 +204,20 @@
    }

    public static final float[] rotate(final float[] v, final float[][] m) {

  •   float x = v[0] * m[0][0] + v[1] * m[0][1] + v[2] * m[0][2];
    
  •   float y = v[0] * m[1][0] + v[1] * m[1][1] + v[2] * m[1][2];
    
  •   float z = v[0] * m[2][0] + v[1] * m[2][1] + v[2] * m[2][2];
    
  •   final float x = v[0] * m[0][0] + v[1] * m[0][1] + v[2] * m[0][2];
    
  •   final float y = v[0] * m[1][0] + v[1] * m[1][1] + v[2] * m[1][2];
    
  •   final float z = v[0] * m[2][0] + v[1] * m[2][1] + v[2] * m[2][2];
      return new float[] { x, y, z };
    

    }

    public static final float[][] calcRotationMatrix(final float ax, final float ay, final float az) {

  •   float[][] retval = new float[3][3];
    
  •   float cax = (float) Math.cos(ax);
    
  •   float sax = (float) Math.sin(ax);
    
  •   float cay = (float) Math.cos(ay);
    
  •   float say = (float) Math.sin(ay);
    
  •   float caz = (float) Math.cos(az);
    
  •   float saz = (float) Math.sin(az);
    
  •   final float[][] retval = new float[3][3];
    
  •   final float cax = (float) Math.cos(ax);
    
  •   final float sax = (float) Math.sin(ax);
    
  •   final float cay = (float) Math.cos(ay);
    
  •   final float say = (float) Math.sin(ay);
    
  •   final float caz = (float) Math.cos(az);
    
  •   final float saz = (float) Math.sin(az);
    
      retval[0][0] = cay * caz;
      retval[0][1] = -cay * saz;
    

@@ -235,10 +233,10 @@
}

public static final float[] normalize(final float[] v) {
  •   float l = ShaderUtils.length(v);
    
  •   float[] r = new float[v.length];
    
  •   final float l = ShaderUtils.length(v);
    
  •   final float[] r = new float[v.length];
      int i = 0;
    
  •   for (float vv : v) {
    
  •   for (final float vv : v) {
      	r[i++] = vv / l;
      }
      return r;
    

@@ -246,7 +244,7 @@

public static final float length(final float[] v) {
	float s = 0;
  •   for (float vv : v) {
    
  •   for (final float vv : v) {
      	s += vv * vv;
      }
      return (float) Math.sqrt(s);
    

@@ -256,17 +254,17 @@
WritableRaster wr;
DataBuffer db;

  •   BufferedImage bi = new BufferedImage(128, 64, BufferedImage.TYPE_INT_ARGB);
    
  •   Graphics2D g = bi.createGraphics();
    
  •   final BufferedImage bi = new BufferedImage(128, 64, BufferedImage.TYPE_INT_ARGB);
    
  •   final Graphics2D g = bi.createGraphics();
      g.drawImage(bufferedImage, null, null);
      bufferedImage = bi;
      wr = bi.getRaster();
      db = wr.getDataBuffer();
    
  •   DataBufferInt dbi = (DataBufferInt) db;
    
  •   int[] data = dbi.getData();
    
  •   final DataBufferInt dbi = (DataBufferInt) db;
    
  •   final int[] data = dbi.getData();
    
  •   ByteBuffer byteBuffer = ByteBuffer.allocateDirect(data.length * 4);
    
  •   final ByteBuffer byteBuffer = BufferUtils.createByteBuffer(data.length * 4);
      byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
      byteBuffer.asIntBuffer().put(data);
      byteBuffer.flip();
    

@@ -274,12 +272,12 @@
return byteBuffer;
}

  • public static float frac(float f) {
  • public static float frac(final float f) {
    return f - ShaderUtils.floor(f);
    }
  • public static float[] floor(float[] fs) {
  •   float[] retval = new float[fs.length];
    
  • public static float[] floor(final float[] fs) {
  •   final float[] retval = new float[fs.length];
      for (int i = 0; i < fs.length; i++) {
      	retval[i] = ShaderUtils.floor(fs[i]);
      }
    
1 Like

+1, nice catch

BufferUtils isn’t tracking memory because I turned that off because the support is horribly broken.



…but still, if JME is going to have such a utility then it should be universally used. :slight_smile:



Though the two lines that actually changed would have been easier to spot without the sea of "final"s. :wink:

Well yeah, sorry its my autoformat settings, that does that.



About the tracking, we have the interface however, I guess I will try to get taht stuff work correctly then soon, since I need it to track down memory leaks I have.

uh…what’s this class…?

Half of it is already implemented elsewhere in the engine…like in FastMath or Quaternion, Matrix4f etc…

And the other half should be put in FastMath really.

except the one that generates images maybe…

Dunno, I just searched for classes, that does not use BufferUtils, in the hope to find a culprit.



But looking over the code I agree, this class needs some refactoring.

@EmpirePhoenix said:
About the tracking, we have the interface however, I guess I will try to get taht stuff work correctly then soon, since I need it to track down memory leaks I have.


Cool. I left a big comment in that file when I disabled the tracking. It even offers an alternative approach to tracking.

...if you reimplement it then please still leave it off by default. At least until it settles in for a while.

I will more likely just use some counters, and a ReferenceQueue to track garbagecollector.



Actually I might try a alternative implementaiton for bufferutils as well when I’m done with that,

I read some post about preventing the directmmeory not being garbagecollected in real time.

One approach was to create one extremly large (allowed limit) buffer at the start of the application, and only use slice() to create smaller buffers where needed. Pro its extremely easy to release object, just tell the Bufferutils its not used anymore and it can instantly mark that space a not used,

Contra: From time to time you might need to move Bytebuffers to prevent fragmentation. Must think about this, but sounds intresting.

Seems to me that the JVM is already doing this. So you might as well just release the memory using the new utility method just added and let the JVM manage the memory.



The only issue with direct memory is that the garbage collector does not use direct memory size to know when to GC. So you could be out of memory and still not cause a GC.



…thus the ability to manually free a buffer becomes very useful since in the cases where a game churns through lots of direct memory, it usually also knows when it’s finished with it.



Any approach based on references or garbage collection runs the risk of running afoul of the same problem direct memory GC already has.



Regarding your comment about tracking direct memory, I sort of remember that being exactly what I recommended in the big comment in BufferUtils. I can’t remember for sure.

You suggested something witha LinkedQueue, but you probably meant the same.



The destroybuffer has a few Problems I see,

It is entirly undokumented, and with a new Java version it might just start to fail.

Its oracle jvm only solution,



Based onn those two I thinks it’s a nice thing to optimize, but not for things that must just work fine.

(also a still missing thing in the current buffertuils is the call to System.gc on outofdirectmemory.



Well i will play a bit around with this, maybee I will produce something usefull. (If so, bufferutils could easily be made a interface to let the user choose wich implementation to use)

re: System.gc on outofdirectmemory.



Mythruna does this when it runs out of memory trying to create direct memory. You have to do it in a loop because you will still get an out of memory after the first GC call (I think I cap it at five tries). Half the time it crashes the app hard… like dump with no error except a JVM log.



And there was a reason I specified LinkedQueue instead of a reference queue but I don’t remember why now.

@pspeed said:
re: System.gc on outofdirectmemory.

Mythruna does this when it runs out of memory trying to create direct memory. You have to do it in a loop because you will still get an out of memory after the first GC call (I think I cap it at five tries). Half the time it crashes the app hard... like dump with no error except a JVM log.


As far as I know there's no way to trick the JVM into doing a sweep of the tenured area so I'd think we're kind of at the mercy of an unknown here..

Yes, thats why I find the solution with the large directbuffer so intresting, as it would go around all those problems, it would devide the memorymanagement models fine.



'One manuall where you have to keep care, and one automatic for the java stuff, without pitfalls like more heapspace makes more outofmemory errors. Definitly worth a try. (Well thre is one other way also possible with the same, kill the direct buffers with a small jni module, that just deletes the buffer like the cleaner does from the c side, but this would lead to the need of a additional native library)

The delete() call works on any existing JVM (except dalvik probably xD)

Here is a somewhat basic version with working Tracking.

and I plan on having a additionalswitch to store EVERY stacktrace to the allocated buffers, and allow to print out wich function has how much currently alive directmemorybuffers allocated. This will help everyone trying to find directmemory leaks.



[java]/*

  • Copyright © 2009-2010 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.ref.PhantomReference;

    import java.lang.ref.ReferenceQueue;

    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.DoubleBuffer;

    import java.nio.FloatBuffer;

    import java.nio.IntBuffer;

    import java.nio.ShortBuffer;

    import java.util.HashMap;

    import java.util.Map.Entry;

    import java.util.Vector;

    import java.util.concurrent.atomic.AtomicInteger;

    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;



    /**
  • <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 AtomicInteger bytesAllocated = new AtomicInteger();

    static final ReferenceQueue collectedBuffers = new ReferenceQueue();

    /**
  • This is needed, else the Reference is collected before the buffer, so it
  • cannot be added to the Referencequeue

    */

    static final Vector<BufferReference> referencetrash = new Vector();



    private static final boolean trackDirectMemory = true;



    /**
  • 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<br />
    
  • @return The cloned buffer

    */

    public static Buffer clone(final Buffer buf) {

    if (buf instanceof FloatBuffer) {

    return clone((FloatBuffer) buf);

    } else if (buf instanceof ShortBuffer) {

    return clone((ShortBuffer) buf);

    } else if (buf instanceof ByteBuffer) {

    return clone((ByteBuffer) buf);

    } else if (buf instanceof IntBuffer) {

    return clone((IntBuffer) buf);

    } else if (buf instanceof DoubleBuffer) {

    return clone((DoubleBuffer) buf);

    } else {

    throw new UnsupportedOperationException();

    }

    }



    private static void onBufferAllocated(final Buffer buffer) {

    if (trackDirectMemory) {

    try {

    bytesAllocated.addAndGet(buffer.capacity());

    final BufferReference a = new BufferReference(buffer);

    referencetrash.add(a);

    cleanupReferenceQueue();



    } catch (final InterruptedException e) {

    e.printStackTrace();

    }

    // System.err.println("Current DirectMemoryBuffer useage " +

    // (bytesAllocated.get() / 1024 / 1024f) + " mb");

    }



    }



    private static void cleanupReferenceQueue() throws InterruptedException {



    while (true) {

    final BufferReference top = (BufferReference) collectedBuffers.poll();

    if (top == null) {

    break;

    }

    referencetrash.remove(top);

    bytesAllocated.addAndGet(-top.getSize());

    }

    }



    /**
  • 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<br />
    

*/

public static FloatBuffer createFloatBuffer(final Vector3f… data) {

if (data == null) {

return null;

}

final 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);

} else {

buff.put(0).put(0).put(0);

}

}

buff.flip();

return buff;

}



/**

  • 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<br />
    

*/

public static FloatBuffer createFloatBuffer(final Quaternion… data) {

if (data == null) {

return null;

}

final 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());

} else {

buff.put(0).put(0).put(0);

}

}

buff.flip();

return buff;

}



/**

  • Generate a new FloatBuffer using the given array of float primitives.

    *
  • @param data
  •        array of float primitives to place into a new FloatBuffer<br />
    

*/

public static FloatBuffer createFloatBuffer(final float… data) {

if (data == null) {

return null;

}

final FloatBuffer buff = createFloatBuffer(data.length);

buff.clear();

buff.put(data);

buff.flip();

return buff;

}



/**

  • 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<br />
    
  •        buffer<br />
    
  • @return the requested new FloatBuffer

    */

    public static FloatBuffer createVector3Buffer(final int vertices) {

    final FloatBuffer vBuff = createFloatBuffer(3 * vertices);

    return vBuff;

    }



    /**
  • 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<br />
    
  • @param vertices
  •        number of vertices that need to be held by the newly created<br />
    
  •        buffer<br />
    
  • @return the requested new FloatBuffer

    */

    public static FloatBuffer createVector3Buffer(final FloatBuffer buf, final int vertices) {

    if (buf != null && buf.limit() == 3 * vertices) {

    buf.rewind();

    return buf;

    }



    return createFloatBuffer(3 * vertices);

    }



    /**
  • Sets the data contained in the given color into the FloatBuffer at the
  • specified index.

    *
  • @param color
  •        the data to insert<br />
    
  • @param buf
  •        the buffer to insert into<br />
    
  • @param index
  •        the postion to place the data; in terms of colors not floats<br />
    

*/

public static void setInBuffer(final ColorRGBA color, final FloatBuffer buf, final int index) {

buf.position(index * 4);

buf.put(color.r);

buf.put(color.g);

buf.put(color.b);

buf.put(color.a);

}



/**

  • Sets the data contained in the given quaternion into the FloatBuffer at
  • the specified index.

    *
  • @param quat
  •        the {@link Quaternion} to insert<br />
    
  • @param buf
  •        the buffer to insert into<br />
    
  • @param index
  •        the postion to place the data; in terms of quaternions not<br />
    
  •        floats<br />
    

*/

public static void setInBuffer(final Quaternion quat, final FloatBuffer buf, final int index) {

buf.position(index * 4);

buf.put(quat.getX());

buf.put(quat.getY());

buf.put(quat.getZ());

buf.put(quat.getW());

}



/**

  • Sets the data contained in the given Vector3F into the FloatBuffer at the
  • specified index.

    *
  • @param vector
  •        the data to insert<br />
    
  • @param buf
  •        the buffer to insert into<br />
    
  • @param index
  •        the postion to place the data; in terms of vectors not floats<br />
    

*/

public static void setInBuffer(final Vector3f vector, final FloatBuffer buf, final int index) {

if (buf == null) {

return;

}

if (vector == null) {

buf.put(index * 3, 0);

buf.put((index * 3) + 1, 0);

buf.put((index * 3) + 2, 0);

} else {

buf.put(index * 3, vector.x);

buf.put((index * 3) + 1, vector.y);

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<br />
    
  • @param buf
  •        the buffer to read from<br />
    
  • @param index
  •        the position (in terms of vectors, not floats) to read from<br />
    
  •        the buf<br />
    

*/

public static void populateFromBuffer(final Vector3f vector, final FloatBuffer buf, final int index) {

vector.x = buf.get(index * 3);

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<br />
    
  • @return a newly generated array of Vector3f objects

    */

    public static Vector3f[] getVector3Array(final FloatBuffer buff) {

    buff.clear();

    final Vector3f[] verts = new Vector3f[buff.limit() / 3];

    for (int x = 0; x < verts.length; x++) {

    final Vector3f v = new Vector3f(buff.get(), buff.get(), buff.get());

    verts[x] = v;

    }

    return verts;

    }



    /**
  • 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<br />
    
  • @param fromPos
  •        the index of the vector to copy<br />
    
  • @param toPos
  •        the index to copy the vector to<br />
    

*/

public static void copyInternalVector3(final FloatBuffer buf, final int fromPos, final int toPos) {

copyInternal(buf, fromPos * 3, toPos * 3, 3);

}



/**

  • Normalize a Vector3f in-buffer.

    *
  • @param buf
  •        the buffer to find the Vector3f within<br />
    
  • @param index
  •        the position (in terms of vectors, not floats) of the vector<br />
    
  •        to normalize<br />
    

*/

public static void normalizeVector3(final FloatBuffer buf, final int index) {

final TempVars vars = TempVars.get();

final Vector3f tempVec3 = vars.vect1;

populateFromBuffer(tempVec3, buf, index);

tempVec3.normalizeLocal();

setInBuffer(tempVec3, buf, index);

vars.release();

}



/**

  • Add to a Vector3f in-buffer.

    *
  • @param toAdd
  •        the vector to add from<br />
    
  • @param buf
  •        the buffer to find the Vector3f within<br />
    
  • @param index
  •        the position (in terms of vectors, not floats) of the vector<br />
    
  •        to add to<br />
    

*/

public static void addInBuffer(final Vector3f toAdd, final FloatBuffer buf, final int index) {

final TempVars vars = TempVars.get();

final Vector3f tempVec3 = vars.vect1;

populateFromBuffer(tempVec3, buf, index);

tempVec3.addLocal(toAdd);

setInBuffer(tempVec3, buf, index);

vars.release();

}



/**

  • Multiply and store a Vector3f in-buffer.

    *
  • @param toMult
  •        the vector to multiply against<br />
    
  • @param buf
  •        the buffer to find the Vector3f within<br />
    
  • @param index
  •        the position (in terms of vectors, not floats) of the vector<br />
    
  •        to multiply<br />
    

*/

public static void multInBuffer(final Vector3f toMult, final FloatBuffer buf, final int index) {

final TempVars vars = TempVars.get();

final Vector3f tempVec3 = vars.vect1;

populateFromBuffer(tempVec3, buf, index);

tempVec3.multLocal(toMult);

setInBuffer(tempVec3, buf, index);

vars.release();

}



/**

  • 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.<br />
    
  • @param buf
  •        the buffer to compare data with<br />
    
  • @param index
  •        the position (in terms of vectors, not floats) of the vector<br />
    
  •        in the buffer to check against<br />
    
  • @return

    */

    public static boolean equals(final Vector3f check, final FloatBuffer buf, final int index) {

    final TempVars vars = TempVars.get();

    final Vector3f tempVec3 = vars.vect1;

    populateFromBuffer(tempVec3, buf, index);

    final boolean eq = tempVec3.equals(check);

    vars.release();

    return eq;

    }



    // // – 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.

    *
  • @param data
  •        array of Vector2f objects to place into a new FloatBuffer<br />
    

*/

public static FloatBuffer createFloatBuffer(final Vector2f… data) {

if (data == null) {

return null;

}

final 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);

} else {

buff.put(0).put(0);

}

}

buff.flip();

return buff;

}



/**

  • 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<br />
    
  •        buffer<br />
    
  • @return the requested new FloatBuffer

    */

    public static FloatBuffer createVector2Buffer(final int vertices) {

    final FloatBuffer vBuff = createFloatBuffer(2 * vertices);

    return vBuff;

    }



    /**
  • 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<br />
    
  • @param vertices
  •        number of vertices that need to be held by the newly created<br />
    
  •        buffer<br />
    
  • @return the requested new FloatBuffer

    */

    public static FloatBuffer createVector2Buffer(final FloatBuffer buf, final int vertices) {

    if (buf != null && buf.limit() == 2 * vertices) {

    buf.rewind();

    return buf;

    }



    return createFloatBuffer(2 * vertices);

    }



    /**
  • Sets the data contained in the given Vector2F into the FloatBuffer at the
  • specified index.

    *
  • @param vector
  •        the data to insert<br />
    
  • @param buf
  •        the buffer to insert into<br />
    
  • @param index
  •        the postion to place the data; in terms of vectors not floats<br />
    

*/

public static void setInBuffer(final Vector2f vector, final FloatBuffer buf, final int index) {

buf.put(index * 2, vector.x);

buf.put((index * 2) + 1, vector.y);

}



/**

  • Updates the values of the given vector from the specified buffer at the
  • index provided.

    *
  • @param vector
  •        the vector to set data on<br />
    
  • @param buf
  •        the buffer to read from<br />
    
  • @param index
  •        the position (in terms of vectors, not floats) to read from<br />
    
  •        the buf<br />
    

*/

public static void populateFromBuffer(final Vector2f vector, final FloatBuffer buf, final int index) {

vector.x = buf.get(index * 2);

vector.y = buf.get(index * 2 + 1);

}



/**

  • Generates a Vector2f array from the given FloatBuffer.

    *
  • @param buff
  •        the FloatBuffer to read from<br />
    
  • @return a newly generated array of Vector2f objects

    */

    public static Vector2f[] getVector2Array(final FloatBuffer buff) {

    buff.clear();

    final Vector2f[] verts = new Vector2f[buff.limit() / 2];

    for (int x = 0; x < verts.length; x++) {

    final Vector2f v = new Vector2f(buff.get(), buff.get());

    verts[x] = v;

    }

    return verts;

    }



    /**
  • 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<br />
    
  • @param fromPos
  •        the index of the vector to copy<br />
    
  • @param toPos
  •        the index to copy the vector to<br />
    

*/

public static void copyInternalVector2(final FloatBuffer buf, final int fromPos, final int toPos) {

copyInternal(buf, fromPos * 2, toPos * 2, 2);

}



/**

  • Normalize a Vector2f in-buffer.

    *
  • @param buf
  •        the buffer to find the Vector2f within<br />
    
  • @param index
  •        the position (in terms of vectors, not floats) of the vector<br />
    
  •        to normalize<br />
    

*/

public static void normalizeVector2(final FloatBuffer buf, final int index) {

final TempVars vars = TempVars.get();

final Vector2f tempVec2 = vars.vect2d;

populateFromBuffer(tempVec2, buf, index);

tempVec2.normalizeLocal();

setInBuffer(tempVec2, buf, index);

vars.release();

}



/**

  • Add to a Vector2f in-buffer.

    *
  • @param toAdd
  •        the vector to add from<br />
    
  • @param buf
  •        the buffer to find the Vector2f within<br />
    
  • @param index
  •        the position (in terms of vectors, not floats) of the vector<br />
    
  •        to add to<br />
    

*/

public static void addInBuffer(final Vector2f toAdd, final FloatBuffer buf, final int index) {

final TempVars vars = TempVars.get();

final Vector2f tempVec2 = vars.vect2d;

populateFromBuffer(tempVec2, buf, index);

tempVec2.addLocal(toAdd);

setInBuffer(tempVec2, buf, index);

vars.release();

}



/**

  • Multiply and store a Vector2f in-buffer.

    *
  • @param toMult
  •        the vector to multiply against<br />
    
  • @param buf
  •        the buffer to find the Vector2f within<br />
    
  • @param index
  •        the position (in terms of vectors, not floats) of the vector<br />
    
  •        to multiply<br />
    

*/

public static void multInBuffer(final Vector2f toMult, final FloatBuffer buf, final int index) {

final TempVars vars = TempVars.get();

final Vector2f tempVec2 = vars.vect2d;

populateFromBuffer(tempVec2, buf, index);

tempVec2.multLocal(toMult);

setInBuffer(tempVec2, buf, index);

vars.release();

}



/**

  • 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.<br />
    
  • @param buf
  •        the buffer to compare data with<br />
    
  • @param index
  •        the position (in terms of vectors, not floats) of the vector<br />
    
  •        in the buffer to check against<br />
    
  • @return

    */

    public static boolean equals(final Vector2f check, final FloatBuffer buf, final int index) {

    final TempVars vars = TempVars.get();

    final Vector2f tempVec2 = vars.vect2d;

    populateFromBuffer(tempVec2, buf, index);

    final boolean eq = tempVec2.equals(check);

    vars.release();

    return eq;

    }



    // // – INT METHODS – ////

    /**
  • 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<br />
    

*/

public static IntBuffer createIntBuffer(final int… data) {

if (data == null) {

return null;

}

final IntBuffer buff = createIntBuffer(data.length);

buff.clear();

buff.put(data);

buff.flip();

return buff;

}



/**

  • Create a new int[] array and populate it with the given IntBuffer’s
  • contents.

    *
  • @param buff
  •        the IntBuffer to read from<br />
    
  • @return a new int array populated from the IntBuffer

    */

    public static int[] getIntArray(final IntBuffer buff) {

    if (buff == null) {

    return null;

    }

    buff.clear();

    final int[] inds = new int[buff.limit()];

    for (int x = 0; x < inds.length; x++) {

    inds[x] = buff.get();

    }

    return inds;

    }



    /**
  • Create a new float[] array and populate it with the given FloatBuffer’s
  • contents.

    *
  • @param buff
  •        the FloatBuffer to read from<br />
    
  • @return a new float array populated from the FloatBuffer

    */

    public static float[] getFloatArray(final FloatBuffer buff) {

    if (buff == null) {

    return null;

    }

    buff.clear();

    final float[] inds = new float[buff.limit()];

    for (int x = 0; x < inds.length; x++) {

    inds[x] = buff.get();

    }

    return inds;

    }



    // // – GENERAL DOUBLE ROUTINES – ////

    /**
  • Create a new DoubleBuffer of the specified size.

    *
  • @param size
  •        required number of double to store.<br />
    
  • @return the new DoubleBuffer

    */

    public static DoubleBuffer createDoubleBuffer(final int size) {

    final DoubleBuffer buf = ByteBuffer.allocateDirect(8 * size).order(ByteOrder.nativeOrder()).asDoubleBuffer();

    buf.clear();

    onBufferAllocated(buf);

    return buf;

    }



    /**
  • 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<br />
    
  • @param size
  •        number of doubles that need to be held by the newly created<br />
    
  •        buffer<br />
    
  • @return the requested new DoubleBuffer

    */

    public static DoubleBuffer createDoubleBuffer(DoubleBuffer buf, final int size) {

    if (buf != null && buf.limit() == size) {

    buf.rewind();

    return buf;

    }



    buf = createDoubleBuffer(size);

    return buf;

    }



    /**
  • Creates a new DoubleBuffer with the same contents as the given
  • 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<br />
    
  • @return the copy

    */

    public static DoubleBuffer clone(final DoubleBuffer buf) {

    if (buf == null) {

    return null;

    }

    buf.rewind();



    DoubleBuffer copy;

    if (buf.isDirect()) {

    copy = createDoubleBuffer(buf.limit());

    } else {

    copy = DoubleBuffer.allocate(buf.limit());

    }

    copy.put(buf);



    return copy;

    }



    // // – GENERAL FLOAT ROUTINES – ////

    /**
  • Create a new FloatBuffer of the specified size.

    *
  • @param size
  •        required number of floats to store.<br />
    
  • @return the new FloatBuffer

    */

    public static FloatBuffer createFloatBuffer(final int size) {

    final FloatBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asFloatBuffer();

    buf.clear();

    onBufferAllocated(buf);

    return buf;

    }



    /**
  • Copies floats from one position in the buffer to another.

    *
  • @param buf
  •        the buffer to copy from/to<br />
    
  • @param fromPos
  •        the starting point to copy from<br />
    
  • @param toPos
  •        the starting point to copy to<br />
    
  • @param length
  •        the number of floats to copy<br />
    

*/

public static void copyInternal(final FloatBuffer buf, final int fromPos, final int toPos, final int length) {

final float[] data = new float[length];

buf.position(fromPos);

buf.get(data);

buf.position(toPos);

buf.put(data);

}



/**

  • Creates a new FloatBuffer with the same contents as the given
  • 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<br />
    
  • @return the copy

    */

    public static FloatBuffer clone(final FloatBuffer buf) {

    if (buf == null) {

    return null;

    }

    buf.rewind();



    FloatBuffer copy;

    if (buf.isDirect()) {

    copy = createFloatBuffer(buf.limit());

    } else {

    copy = FloatBuffer.allocate(buf.limit());

    }

    copy.put(buf);



    return copy;

    }



    // // – GENERAL INT ROUTINES – ////

    /**
  • Create a new IntBuffer of the specified size.

    *
  • @param size
  •        required number of ints to store.<br />
    
  • @return the new IntBuffer

    */

    public static IntBuffer createIntBuffer(final int size) {

    final IntBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asIntBuffer();

    buf.clear();

    onBufferAllocated(buf);

    return buf;

    }



    /**
  • 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<br />
    
  • @param size
  •        number of ints that need to be held by the newly created<br />
    
  •        buffer<br />
    
  • @return the requested new IntBuffer

    */

    public static IntBuffer createIntBuffer(IntBuffer buf, final int size) {

    if (buf != null && buf.limit() == size) {

    buf.rewind();

    return buf;

    }



    buf = createIntBuffer(size);

    return buf;

    }



    /**
  • Creates a new IntBuffer with the same contents as the given IntBuffer.
  • 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<br />
    
  • @return the copy

    */

    public static IntBuffer clone(final IntBuffer buf) {

    if (buf == null) {

    return null;

    }

    buf.rewind();



    IntBuffer copy;

    if (buf.isDirect()) {

    copy = createIntBuffer(buf.limit());

    } else {

    copy = IntBuffer.allocate(buf.limit());

    }

    copy.put(buf);



    return copy;

    }



    // // – GENERAL BYTE ROUTINES – ////

    /**
  • Create a new ByteBuffer of the specified size.

    *
  • @param size
  •        required number of ints to store.<br />
    
  • @return the new IntBuffer

    */

    public static ByteBuffer createByteBuffer(final int size) {

    final ByteBuffer buf = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());

    buf.clear();

    onBufferAllocated(buf);

    return buf;

    }



    /**
  • 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<br />
    
  • @param size
  •        number of bytes that need to be held by the newly created<br />
    
  •        buffer<br />
    
  • @return the requested new IntBuffer

    */

    public static ByteBuffer createByteBuffer(ByteBuffer buf, final int size) {

    if (buf != null && buf.limit() == size) {

    buf.rewind();

    return buf;

    }



    buf = createByteBuffer(size);

    return buf;

    }



    public static ByteBuffer createByteBuffer(final byte… data) {

    final ByteBuffer bb = createByteBuffer(data.length);

    bb.put(data);

    bb.flip();

    return bb;

    }



    public static ByteBuffer createByteBuffer(final String data) {

    final byte[] bytes = data.getBytes();

    final ByteBuffer bb = createByteBuffer(bytes.length);

    bb.put(bytes);

    bb.flip();

    return bb;

    }



    /**
  • Creates a new ByteBuffer with the same contents as the given ByteBuffer.
  • 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<br />
    
  • @return the copy

    */

    public static ByteBuffer clone(final ByteBuffer buf) {

    if (buf == null) {

    return null;

    }

    buf.rewind();



    ByteBuffer copy;

    if (buf.isDirect()) {

    copy = createByteBuffer(buf.limit());

    } else {

    copy = ByteBuffer.allocate(buf.limit());

    }

    copy.put(buf);



    return copy;

    }



    // // – GENERAL SHORT ROUTINES – ////

    /**
  • Create a new ShortBuffer of the specified size.

    *
  • @param size
  •        required number of shorts to store.<br />
    
  • @return the new ShortBuffer

    */

    public static ShortBuffer createShortBuffer(final int size) {

    final ShortBuffer buf = ByteBuffer.allocateDirect(2 * size).order(ByteOrder.nativeOrder()).asShortBuffer();

    buf.clear();

    onBufferAllocated(buf);

    return buf;

    }



    /**
  • 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<br />
    
  • @param size
  •        number of shorts that need to be held by the newly created<br />
    
  •        buffer<br />
    
  • @return the requested new ShortBuffer

    */

    public static ShortBuffer createShortBuffer(ShortBuffer buf, final int size) {

    if (buf != null && buf.limit() == size) {

    buf.rewind();

    return buf;

    }



    buf = createShortBuffer(size);

    return buf;

    }



    public static ShortBuffer createShortBuffer(final short… data) {

    if (data == null) {

    return null;

    }

    final ShortBuffer buff = createShortBuffer(data.length);

    buff.clear();

    buff.put(data);

    buff.flip();

    return buff;

    }



    /**
  • Creates a new ShortBuffer with the same contents as the given
  • ShortBuffer. 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<br />
    
  • @return the copy

    */

    public static ShortBuffer clone(final ShortBuffer buf) {

    if (buf == null) {

    return null;

    }

    buf.rewind();



    ShortBuffer copy;

    if (buf.isDirect()) {

    copy = createShortBuffer(buf.limit());

    } else {

    copy = ShortBuffer.allocate(buf.limit());

    }

    copy.put(buf);



    return copy;

    }



    /**
  • 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)<br />
    
  • @param required
  •        minimum number of elements that should be remaining in the<br />
    
  •        returned buffer<br />
    
  • @return a buffer large enough to receive at least the
  •     &lt;code&gt;required&lt;/code&gt; number of entries, same position as the<br />
    
  •     input buffer, not null<br />
    

*/

public static FloatBuffer ensureLargeEnough(FloatBuffer buffer, final int required) {

if (buffer == null || (buffer.remaining() < required)) {

final int position = (buffer != null ? buffer.position() : 0);

final FloatBuffer newVerts = createFloatBuffer(position + required);

if (buffer != null) {

buffer.rewind();

newVerts.put(buffer);

newVerts.position(position);

}

buffer = newVerts;

}

return buffer;

}



public static ShortBuffer ensureLargeEnough(ShortBuffer buffer, final int required) {

if (buffer == null || (buffer.remaining() < required)) {

final int position = (buffer != null ? buffer.position() : 0);

final ShortBuffer newVerts = createShortBuffer(position + required);

if (buffer != null) {

buffer.rewind();

newVerts.put(buffer);

newVerts.position(position);

}

buffer = newVerts;

}

return buffer;

}



public static ByteBuffer ensureLargeEnough(ByteBuffer buffer, final int required) {

if (buffer == null || (buffer.remaining() < required)) {

final int position = (buffer != null ? buffer.position() : 0);

final ByteBuffer newVerts = createByteBuffer(position + required);

if (buffer != null) {

buffer.rewind();

newVerts.put(buffer);

newVerts.position(position);

}

buffer = newVerts;

}

return buffer;

}



public static void printCurrentDirectMemory(StringBuilder store) {

try {

cleanupReferenceQueue();



// make a new set to hold the keys to prevent concurrency issues.

final int totalHeld = bytesAllocated.get();

final int fBufs = 0, bBufs = 0, iBufs = 0, sBufs = 0, dBufs = 0;

final long heapMem = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();



final boolean printStout = store == null;

if (store == null) {

store = new StringBuilder();

}

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("kbn");

store.append("Total direct memory held: ").append(totalHeld / 1024).append("kbn");

final HashMap<String, Integer> bufferdata = new HashMap<String, Integer>();

for (final BufferReference b : referencetrash) {

Integer size = bufferdata.get(b.getType());

if (size == null) {

size = 0;

}

size += b.getSize();

bufferdata.put(b.getType(), size);

}

for (final Entry<String, Integer> typedata : bufferdata.entrySet()) {

store.append(typedata.getKey()).append(" ").append(typedata.getValue() / 1024 / 1024f).append("mbn");



}

if (printStout) {

System.out.println(store.toString());

}

} catch (final Exception e) {

e.printStackTrace();

}

}



/**

  • DirectByteBuffers 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 DirectByteBuffers. However, as this doesn’t happen
  • immediately after discarding all references to a DirectByteBuffer, it’s
  • easy to OutOfMemoryError yourself using DirectByteBuffers. This function
  • explicitly calls the Cleaner method of a DirectByteBuffer.

    *
  • @param toBeDestroyed
  •        The DirectByteBuffer that will be &quot;cleaned&quot;. Utilizes<br />
    
  •        reflection.<br />
    

*

*/

public static void destroyByteBuffer(final ByteBuffer toBeDestroyed) {

try {

final Method cleanerMethod = toBeDestroyed.getClass().getMethod("cleaner");

cleanerMethod.setAccessible(true);

final Object cleaner = cleanerMethod.invoke(toBeDestroyed);

final Method cleanMethod = cleaner.getClass().getMethod("clean");

cleanMethod.setAccessible(true);

cleanMethod.invoke(cleaner);

} catch (final IllegalAccessException ex) {

Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);

} catch (final IllegalArgumentException ex) {

Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);

} catch (final InvocationTargetException ex) {

Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);

} catch (final NoSuchMethodException ex) {

Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);

} catch (final SecurityException ex) {

Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);

}

}



}



class BufferReference extends PhantomReference<Buffer> {

private final int size;

private final String type;



public BufferReference(final Buffer referent) {

super(referent, BufferUtils.collectedBuffers);

this.size = referent.capacity();

this.type = referent.getClass().getSimpleName();

}



public int getSize() {

return size;

}



public String getType() {

return type;

}

}

[/java]