This looks like a clever implementation. I'll try to wire it in and see what sort of numbers I get on my microbenchmarks.

On 6/6/06, Chris Nokleberg < [EMAIL PROTECTED]> wrote:
I've attached a lightly-tested sample alternative ObjectSpace
implementation that you might use for inspiration. It is pure-Java (no
JRuby stuff) so you'll have to adapt it.

The key points are:

- A list of arrays of references is used instead of just a list of
  references. This gives you more control by letting you do a cleanup only
  when you are about to allocate another array. It also avoids the
  internal array copying that happens when an ArrayList is resized.

- It uses a BitSet to keep track of which indexes are used. During cleanup
  it iterates over all of the used indexes and checks to see if each
  WeakReference value has been collected.

I wasn't sure if the order of the objects returned by iterator matters, if
so you'd have to compact the arrays in cleanup instead of just nulling out
unused slots. This would make it easier to discard entirely empty arrays,
but it would probably be a net loss performance-wise (and more complex).

There are some alternative cleanup implementations, for example creating a
"WeakRefWithIndex" class that extends WeakReference and keeps track of the
array index. Cleanup then would poll a ReferenceQueue and zero out the
indexes of the dead references. I tried this and unsurprisingly it uses
less total memory (because indexes are freed ASAP) but is slower overall.

I've compared the performance to the existing JRuby code and this is about
two times faster on my machine. I hope there is more that can be done but
honestly reducing the number of objects that get registered with
ObjectSpace is probably the best bet (e.g. perhaps you could detect if any
client code will ever call iterator).

You could also defer creation of the ObjectSpace until it is requested for
the first time and then build it by leveraging the JVM profiler support
(walk all live objects looking for JRuby ones). I'm not volunteering :-)

Chris

====================================================================
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.* ;

public class ObjectSpace
{
    private static final int CHUNK_SIZE = 0x2000;

    private List chunks = new ArrayList();
    private BitSet bits = new BitSet();
    private int clearIndex;
    private int max;

    synchronized public void add(Object obj) {
        int index = bits.nextClearBit(clearIndex);
        if (index >= max) {
            cleanup();
            index = bits.nextClearBit(0);
            if (index >= max) {
                // all slots are filled, allocate new chunk
                chunks.add(new WeakReference[CHUNK_SIZE]);
                max = chunks.size() * CHUNK_SIZE;
            }
        }
        ((WeakReference[])chunks.get(index / CHUNK_SIZE))[index % CHUNK_SIZE] = new WeakReference(obj);
        bits.set(index);
        clearIndex = index + 1;
    }

    private void cleanup() {
        for (int i = bits.nextSetBit(0); i >= 0; i = bits.nextSetBit(i + 1)) {
            WeakReference[] chunk = (WeakReference[])chunks.get(i / CHUNK_SIZE);
            if (chunk[i % CHUNK_SIZE].get() == null) {
                chunk[i % CHUNK_SIZE] = null;
                bits.clear(i);
            }
        }
    }

    public Iterator iterator() {
        return new ObjectSpaceIterator();
    }

    private class ObjectSpaceIterator implements Iterator
    {
        private int setIndex;
        private Object next;

        public ObjectSpaceIterator() {
            advance();
        }

        public boolean hasNext() {
            return next != null;
        }

        public Object next() {
            Object result = next;
            advance();
            return next;
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

        private void advance() {
            next = null;
            synchronized (ObjectSpace.this) {
                while (next == null) {
                    int index = bits.nextSetBit(setIndex);
                    if (index == -1)
                        return;
                    setIndex = index + 1;
                    WeakReference ref = ((WeakReference[])chunks.get(index / CHUNK_SIZE))[index % CHUNK_SIZE];
                    if (ref != null)
                        next = ref.get();
                }
            }
        }
    }
}



_______________________________________________
Jruby-devel mailing list
Jruby-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jruby-devel



--
Charles Oliver Nutter @ headius.blogspot.com
JRuby Developer @ jruby.sourceforge.net
Application Architect @ www.ventera.com
_______________________________________________
Jruby-devel mailing list
Jruby-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jruby-devel

Reply via email to