diff --git a/jme3-lwjgl3/src/main/java/com/jme3/util/LWJGLBufferAllocator.java b/jme3-lwjgl3/src/main/java/com/jme3/util/LWJGLBufferAllocator.java index 5d65004cb..e654a6d35 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/util/LWJGLBufferAllocator.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/util/LWJGLBufferAllocator.java @@ -2,8 +2,12 @@ package com.jme3.util; import org.lwjgl.system.MemoryUtil; +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; import java.nio.Buffer; import java.nio.ByteBuffer; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * The implementation of the {@link BufferAllocator} which use {@link MemoryUtil} to manage memory. @@ -12,13 +16,91 @@ import java.nio.ByteBuffer; */ public class LWJGLBufferAllocator implements BufferAllocator { + /** + * The reference queue. + */ + private static final ReferenceQueue DUMMY_QUEUE = new ReferenceQueue(); + + /** + * The LWJGL byte buffer deallocator. + */ + private static class Deallocator extends PhantomReference { + + /** + * The address of LWJGL byte buffer. + */ + private Long address; + + public Deallocator(final ByteBuffer referent, final ReferenceQueue queue, + final Long address) { + super(referent, queue); + this.address = address; + } + + /** + * @param address the address of LWJGL byte buffer. + */ + void setAddress(final Long address) { + this.address = address; + } + + /** + * Free memory. + */ + void free() { + if (address == null) return; + MemoryUtil.nmemFree(address); + DEALLOCATORS.remove(address); + } + } + + /** + * The cleaner thread. + */ + private static final Thread CLEAN_THREAD = new Thread(LWJGLBufferAllocator::freeByteBuffers); + + /** + * The map with created deallocators. + */ + private static final Map DEALLOCATORS = new ConcurrentHashMap<>(); + + static { + CLEAN_THREAD.setDaemon(true); + CLEAN_THREAD.setName("Thread to free LWJGL byte buffers"); + CLEAN_THREAD.start(); + } + + /** + * Free unnecessary LWJGL byte buffers. + */ + private static void freeByteBuffers() { + try { + for (;;) { + final Deallocator deallocator = (Deallocator) DUMMY_QUEUE.remove(); + deallocator.free(); + } + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + @Override public void destroyDirectBuffer(final Buffer buffer) { + + final long address = MemoryUtil.memAddress((ByteBuffer) buffer); + + // disable deallocator + final Deallocator deallocator = DEALLOCATORS.remove(address); + if (deallocator != null) deallocator.setAddress(null); + MemoryUtil.memFree(buffer); } @Override public ByteBuffer allocate(final int size) { - return MemoryUtil.memAlloc(size); + final Long address = MemoryUtil.nmemAlloc(size); + final ByteBuffer byteBuffer = MemoryUtil.memByteBuffer(address, size); + DEALLOCATORS.put(address, new Deallocator(byteBuffer, DUMMY_QUEUE, address)); + return byteBuffer; } }