Skip to main content

A Dip in the Autopool

Posted by mason on October 3, 2004 at 12:05 AM PDT

Object pooling in Java is generally a bad idea for many reasons, not the least of which it has the nasty ability to actually cause the very performance problems you are trying to solve on modern JVMs, but there are always cases where the resources available to your application are in such limited supply that you really don't have a choice in the matter. Instances are rare, but in some cases you just pay too great a resource cost for each object and a pool is required.


I ran across just such a situation while redesigning my personal website, and seeing how it was personal, I had a lot of leeway to experiment. One small annoyance kept popping up time and time again. If you ever forgot to return the object to the pool, you’d lose it forever… unless you built one of many various object timeout schemes. The problem at that point was how to also build in a kind of "keep-alive" system that would allow you to check out objects past the default timeout period and not have them reclaimed while still in use. Needless to say, this entire system was turning into a very fine mess in short order making me think that this small little personal project was going to become an uncomfortably tiresome endeavor. If there was only a way, i thought, to watch these objects go out of scope and reclaim them. As it turns out, just such a thing is possible, with a little relfection wrangling and thus was born the Edgecase autopool.


Note: forgive any formatting or other inconsistancies. I've chopped up the final version for instructive purposes and removed anything that is not absolutly needed, so I am not 100% if it's even compilable in it's current state (you can try by removing all the bold comments below), but a more complete version can be found in the edgecase project.

public class Pool extends ArrayList {

    private static ReferenceQueue _refqueue = new ReferenceQueue();
    private static HashMap _refs = new HashMap();
    private static WeakHashMap _reflookup = new WeakHashMap();

    private Class _pooledClass = null;

    static {
        runPooler();
    }

The usage is quite simple. To create a new pool of objects (sorry, this version can only handle classes with default constructors, and no inner classes!) just create a new Pool(YourObject.class);
    public Pool(Class objectclass) {
_pooledClass = objectclass;
    }
This is the thread that will reclaim objects as they go out of scope. The reference queue will become populated as the objects are noticed by the garbage collector and then this thread will yank them from the jaws of death and place them back in the pool.
    private static void runPooler() {
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    try {
                        Reference ref = _refqueue.remove();
                        if (ref != null) {
                            Pool p = (Pool) _refs.remove(ref);
                            if (p != null) {
                                Object o = ref.get();
                                if (o != null) {
                                    p.reclaim(o);
                                }
                            }
                        }
                    } catch (Exception ex) {
                    }
                }
            }
        }, "Object Pooler").start();
    }
To get new objects out of the pool you have created, it's as simple as (YourClass)pool.getNext(). Notice if your object implements the Poolable interface, the init() method will be called.
    public synchronized Object getNext() throws Exception {
if (this.isEmpty()) {
            add(_pooledClass.newInstance());
        }
Object o = this.remove(0);
if (o instanceof Poolable) {
    ((Poolable) o).init();
}
if (!(o instanceof Unreclaimable)) {
    PoolableReference ref = new PoolableReference(o, _refqueue);
    _refs.put(ref, this);
            _reflookup.put(this, ref);
}
return o;
    }
Letting the pool automatically reclaim objects is nice, but it means waiting for the garbage collector to get around to finding PhantomReferences, something that can take a while. It's always polite to reclaim your pool instances manually when you have a need for quick turnaround, so I left it public. Notice that Poolable objects will automatically be clear()ed
    public void reclaim(Object object) {
if (object != null && !(object instanceof Unpoolable) && !this.contains(object)) {
    if (object instanceof Poolable) {
((Poolable) object).clear();
    }
    this.add(0, object);
            synchronized (this) {
this.notifyAll();
    }
        }
    }
Sometimes you are completely finished with an object. But how can you throw it away when it is just going to pop back in the pool when you do? pool.destroy(YourObject) will clean up objects you wish to never return.
    public void destroy(Object o) throws Throwable {
        PoolableReference ref;
        while ((ref = (PoolableReference) _reflookup.remove(o)) != null) {
            if (ref != null) {
                _refs.remove(ref);
                ref.clear();
            }
        }
        while (remove(o)) ;
        if (o instanceof PooledObject) {
    ((PooledObject) o).refinalize();
}
    }
These interfaces help making generic object pooling viable. A Poolable class with automatically be initialized when created and cleared when returned. A Unpoolable or Unreclaimable class is one who's internal state does not allow it to be pooled or automatically reclaimed, such as Threads that have already run, etc.
    public interface Poolable {

        public void init();
        public void clear();

    }

    public interface Unpoolable {}
    public interface Unreclaimable {}
The PoolableReference is the class that make auto reclaimation possible. It is simply a PhantomReference that allows it's internally kept "referant" to be accessed by the outside world. The primary drawback of this class is that objects will be finalized before being reclaimed the first time, and never again. This is not absolutly terrible in all cases, but should be noted so that any strange behavior can be anticipated.
    public static class PoolableReference extends PhantomReference {

private static Field _referentField = null;

static {
    try {
_referentField = Reference.class.getDeclaredField("referent");
_referentField.setAccessible(true);
    } catch (Exception ex) {
                ex.printStackTrace();
    }
}

public PoolableReference(Object referent, ReferenceQueue q) {
    super(referent, q);
}

public Object get() {
    Object ret = null;
    try {
ret = _referentField.get(this);
            } catch (Exception ex) {
                ex.printStackTrace();
    }
    return ret;
}

    }

Finally, a simple extendible class to simplify the entire procedure. It has an empty finalize() method, which has been marked as final to keep anyone from attempting to replace it, that will do nothing when the object is reclaimed the first time and a refinalize() that will call the actual finalization and should be overridden if finalization is desired. If you already have a subclass of PooledObject an internal pooling is already set up, instead of new MyPooledObject(); just use MyPooledObject.createNew(MyPooledObject.class); and the new object will be pulled from the appropriate pool.
    public static class PooledObject implements Pool.Poolable, Serializable {

        private static Hashtable _pools = new Hashtable();
        private Pool _pool = null;

        public PooledObject() {
        }

        public void clear() {
        }

        public void init() {
            clear();
        }

        public void dismiss() {
            _pool.reclaim(this);
        }

        public static PooledObject createNew(Class c) throws Exception {
            if (!_pools.contains(c)) {
                _pools.put(c, new Pool(c));
            }
            Pool p = (Pool) _pools.get(c);
            PooledObject d = (PooledObject) p.getNext();
            d._pool = p;
            return d;
        }

        protected final void finalize() throws Throwable {
        }

        protected void refinalize() throws Throwable {
            super.finalize();
        }

    }
}
Related Topics >>