package proj.zoie.api.impl.util;
import java.lang.ref.WeakReference;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.log4j.Logger;
/**
* @author "Xiaoyang Gu<xgu@linkedin.com>"
*
*/
public class MemoryManager<T> {
private static final Logger log = Logger.getLogger(MemoryManager.class.getName());
private final ConcurrentHashMap<Integer, ConcurrentLinkedQueue<WeakReference<T>>> _sizeMap = new ConcurrentHashMap<Integer, ConcurrentLinkedQueue<WeakReference<T>>>();
private final ConcurrentLinkedQueue<T> _releaseQueue = new ConcurrentLinkedQueue<T>();
private final AtomicInteger _releaseQueueSize = new AtomicInteger(0);
private Initializer<T> _initializer;
private final Thread _cleanThread;
public MemoryManager(Initializer<T> initializer) {
this._initializer = initializer;
_cleanThread = new Thread(new Runnable() {
public void run() {
T buf = null;
while (true) {
synchronized (MemoryManager.this) {
try {
MemoryManager.this.wait(200);
} catch (InterruptedException e) {
log.error(e);
}
}
while ((buf = _releaseQueue.poll()) != null) {
ConcurrentLinkedQueue<WeakReference<T>> queue = _sizeMap.get(_initializer.size(buf));
// buf is wrapped in WeakReference. this allows GC to reclaim the buffer memory
_initializer.init(buf);// pre-initializing the buffer in parallel so we save time when
// it is requested later.
queue.offer(new WeakReference<T>(buf));
_releaseQueueSize.decrementAndGet();
}
buf = null;
}
}
});
_cleanThread.setDaemon(true);
_cleanThread.start();
}
/**
* @return an initialized instance of type T.
*/
public T get(int size) {
size = Integer.highestOneBit(2 * size);
ConcurrentLinkedQueue<WeakReference<T>> queue = _sizeMap.get(size);
if (queue == null) {
queue = new ConcurrentLinkedQueue<WeakReference<T>>();
_sizeMap.putIfAbsent(size, queue);
}
while (true) {
WeakReference<T> ref = (WeakReference<T>) queue.poll();
if (ref != null) {
T buf = ref.get();
if (buf != null) {
return buf;
}
} else {
return _initializer.newInstance(size);
}
}
}
/**
* return the instance to the manager after use
* @param buf
*/
public void release(T buf) {
if (_releaseQueueSize.get() > 1000) {
log.info("release queue full");
return;
}
if (buf != null) {
_releaseQueue.offer(buf);
_releaseQueueSize.incrementAndGet();
synchronized (MemoryManager.this) {
MemoryManager.this.notifyAll();
}
}
}
public static interface Initializer<E> {
public E newInstance(int size);
public int size(E buf);
public void init(E buf);
}
}