Example to utilizing the java.lang.ref.PhantomReference<T>.
Note: the following is a pseudo-code intended to show off the interface and the implementation design.
1) GarbageCollectibleBuffer: A GC PhantomReference wrapping a native buffer.
public class GarbageCollectibleBuffer extends PhantomReference<Buffer> {
private final Buffer referent;
private final long address;
private GarbageCollectibleBuffer(final Buffer referent, final ReferenceQueue<? super Buffer> queue) {
super(referent, queue);
this.referent = referent;
this.address = NativeBufferUtils.getMemoryAddress(buffer);
}
public static GarbageCollectibleBuffer from(final Buffer buffer, final ReferenceQueue<? super Buffer> queue) {
if (!buffer.isDirect()) {
throw new UnSupportedBufferException("Target Buffer isnot a direct Buffer!");
}
return new GarbageCollectibleBuffer(buffer, queue);
}
public static GarbageCollectibleBuffer allocateDirect(final long size, final ReferenceQueue<? super Buffer> queue) {
final ByteBuffer buffer = NativeBufferUtils.clearAlloc(size);
return GarbageCollectibleBuffer.from(buffer, queue);
}
public void deallocate() {
NativeBufferUtils.destroy(referent);
}
public Buffer getReferent() {
return referent;
}
public long getDirectBufferAddress() {
return address;
}
}
2) GarbageCollectableBuffers: A collection of direct collectible buffers.
public final class GarbageCollectibleBuffers {
private static ReferenceQueue<Buffer> COLLECTIBLES = new ReferenceQueue<>();
private final List<GarbageCollectableBuffer> buffers = new ArrayList<>();
private GarbageCollectibleBuffers() {
}
public static GarbageCollectibleBuffer allocate(final long size) {
final GarbageCollectibleBuffer collectible = GarbageCollectibleBuffer.allocateDirect(size, COLLECTIBLES);
buffers.add(collectible);
return collectible;
}
public static GarbageCollectibleBuffer register(final Buffer buffer) {
if (!buffer.isDirect()) {
throw new UnSupportedBufferException("Buffer isn't a direct buffer!");
}
final GarbageCollectibleBuffer collectible = GarbageCollectibleBuffer.from(buffer, COLLECTIBLES);
buffers.add(collectible);
return collectible;
}
public static void deallocate(final Buffer buffer) {
if (!buffers.contains(buffer)) {
throw new UnSupportedBufferException("Buffer is not a direct collectible!");
}
NativeBufferUtils.destroy(buffer);
buffers.remove(collectible);
}
public static MemoryScavenger startMemoryScavenger() {
return MemoryScavenger.start(COLLECTIBLES);
}
}
3) MemoryScavenger: A memory cleaner to help GC scavenge the native memory.
public final class MemoryScavenger extends Thread {
private final ReferenceQueue<? super Buffer> queue;
private MemoryScavenger(final ReferenceQueue<? super Buffer> queue) {
super(MemoryScavenger.class.getName());
setDaemon(true);
this.queue = queue;
}
public static MemoryScavenger start(final ReferenceQueue<? super Buffer> queue) {
final MemoryScavenger scavenger = new MemoryScavenger(queue);
scavenger.start();
return scavenger;
}
@Override
public void run() {
for (;;) {
// blocks until an object is available in the queue before returning and removing it
// object references are added to the queue by the GC as a part of post-mortem actions
GarbageCollectibleBuffer collectible = (GarbageCollectibleBuffer) queue.remove();
collectible.deallocate();
}
}
}
Example to utilizing the
java.lang.ref.PhantomReference<T>
.1) GarbageCollectibleBuffer: A GC PhantomReference wrapping a native buffer.
2) GarbageCollectableBuffers: A collection of direct collectible buffers.
3) MemoryScavenger: A memory cleaner to help GC scavenge the native memory.
------------------------------------------------ JME ------------------------------------------------
4) Implementing the above interface in jme would be super-easy:
LwjglBufferAllocator: A direct buffer allocator/deallocator for LWJGL-2.
AndroidNativeBufferAllocator: A native buffer allocator/deallocator for android.