You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by mr...@apache.org on 2006/02/17 16:01:13 UTC

svn commit: r378539 - in /incubator/jackrabbit/trunk/jackrabbit: ./ src/main/java/org/apache/jackrabbit/core/state/ src/main/java/org/apache/jackrabbit/util/

Author: mreutegg
Date: Fri Feb 17 07:01:11 2006
New Revision: 378539

URL: http://svn.apache.org/viewcvs?rev=378539&view=rev
Log:
- Use custom collection implementation for item state listeners.
reduces memory consumption caused by those collections to about 1/3.
Performance of common operations on the WeakIdentityCollection scale linearly and outperforms the previously used ReferenceMap until it contains aprox. 100 listeners. Most item states only contain few listeners.

Added:
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/WeakIdentityCollection.java   (with props)
Modified:
    incubator/jackrabbit/trunk/jackrabbit/project.properties
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ItemState.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/NodeState.java

Modified: incubator/jackrabbit/trunk/jackrabbit/project.properties
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/project.properties?rev=378539&r1=378538&r2=378539&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/project.properties (original)
+++ incubator/jackrabbit/trunk/jackrabbit/project.properties Fri Feb 17 07:01:11 2006
@@ -32,7 +32,7 @@
 maven.junit.sysproperties=derby.system.durability
 derby.system.durability=test
 #java.security.auth.login.config=applications/test/jaas.config
-maven.junit.jvmargs=-Xmx128m
+maven.junit.jvmargs=-Xmx128m -enableassertions
 
 #If you wish to skip tests when doing builds, uncomment
 #maven.test.skip = true

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ItemState.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ItemState.java?rev=378539&r1=378538&r2=378539&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ItemState.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ItemState.java Fri Feb 17 07:01:11 2006
@@ -16,18 +16,16 @@
  */
 package org.apache.jackrabbit.core.state;
 
-import org.apache.commons.collections.map.ReferenceMap;
 import org.apache.jackrabbit.core.ItemId;
 import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.util.WeakIdentityCollection;
 import org.apache.log4j.Logger;
 
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Map;
+import java.util.Collection;
 
 /**
  * <code>ItemState</code> represents the state of an <code>Item</code>.
@@ -89,9 +87,7 @@
     /**
      * Listeners (weak references)
      */
-    private final transient Map listeners =
-            Collections.synchronizedMap(
-                    new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK));
+    private final transient Collection listeners = new WeakIdentityCollection(5);
 
     /**
      * the backing persistent item state (may be null)
@@ -176,7 +172,9 @@
      */
     void onDisposed() {
         // prepare this instance so it can be gc'ed
-        listeners.clear();
+        synchronized (listeners) {
+            listeners.clear();
+        }
         disconnect();
         overlayedState = null;
         status = STATUS_UNDEFINED;
@@ -223,11 +221,9 @@
      */
     protected void notifyStateDiscarded() {
         // copy listeners to array to avoid ConcurrentModificationException
-        ItemStateListener[] la = new ItemStateListener[listeners.size()];
-        Iterator iter = listeners.values().iterator();
-        int cnt = 0;
-        while (iter.hasNext()) {
-            la[cnt++] = (ItemStateListener) iter.next();
+        ItemStateListener[] la;
+        synchronized (listeners) {
+            la = (ItemStateListener[]) listeners.toArray(new ItemStateListener[listeners.size()]);
         }
         for (int i = 0; i < la.length; i++) {
             if (la[i] != null) {
@@ -242,11 +238,9 @@
      */
     protected void notifyStateCreated() {
         // copy listeners to array to avoid ConcurrentModificationException
-        ItemStateListener[] la = new ItemStateListener[listeners.size()];
-        Iterator iter = listeners.values().iterator();
-        int cnt = 0;
-        while (iter.hasNext()) {
-            la[cnt++] = (ItemStateListener) iter.next();
+        ItemStateListener[] la;
+        synchronized (listeners) {
+            la = (ItemStateListener[]) listeners.toArray(new ItemStateListener[listeners.size()]);
         }
         for (int i = 0; i < la.length; i++) {
             if (la[i] != null) {
@@ -261,11 +255,9 @@
      */
     public void notifyStateUpdated() {
         // copy listeners to array to avoid ConcurrentModificationException
-        ItemStateListener[] la = new ItemStateListener[listeners.size()];
-        Iterator iter = listeners.values().iterator();
-        int cnt = 0;
-        while (iter.hasNext()) {
-            la[cnt++] = (ItemStateListener) iter.next();
+        ItemStateListener[] la;
+        synchronized (listeners) {
+            la = (ItemStateListener[]) listeners.toArray(new ItemStateListener[listeners.size()]);
         }
         for (int i = 0; i < la.length; i++) {
             if (la[i] != null) {
@@ -280,11 +272,9 @@
      */
     protected void notifyStateDestroyed() {
         // copy listeners to array to avoid ConcurrentModificationException
-        ItemStateListener[] la = new ItemStateListener[listeners.size()];
-        Iterator iter = listeners.values().iterator();
-        int cnt = 0;
-        while (iter.hasNext()) {
-            la[cnt++] = (ItemStateListener) iter.next();
+        ItemStateListener[] la;
+        synchronized (listeners) {
+            la = (ItemStateListener[]) listeners.toArray(new ItemStateListener[listeners.size()]);
         }
         for (int i = 0; i < la.length; i++) {
             if (la[i] != null) {
@@ -441,8 +431,9 @@
      * @param listener the new listener to be informed on modifications
      */
     public void addListener(ItemStateListener listener) {
-        if (!listeners.containsKey(listener)) {
-            listeners.put(listener, listener);
+        synchronized (listeners) {
+            assert (!listeners.contains(listener));
+            listeners.add(listener);
         }
     }
 
@@ -452,7 +443,9 @@
      * @param listener an existing listener
      */
     public void removeListener(ItemStateListener listener) {
-        listeners.remove(listener);
+        synchronized (listeners) {
+            listeners.remove(listener);
+        }
     }
 
     //----------------------------------------------------< ItemStateListener >

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/NodeState.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/NodeState.java?rev=378539&r1=378538&r2=378539&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/NodeState.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/NodeState.java Fri Feb 17 07:01:11 2006
@@ -19,11 +19,12 @@
 import org.apache.commons.collections.MapIterator;
 import org.apache.commons.collections.OrderedMapIterator;
 import org.apache.commons.collections.map.LinkedMap;
-import org.apache.commons.collections.map.ReferenceMap;
 import org.apache.jackrabbit.core.NodeId;
 import org.apache.jackrabbit.core.ItemId;
 import org.apache.jackrabbit.core.nodetype.NodeDefId;
 import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.util.WeakIdentityCollection;
+import org.apache.log4j.Logger;
 
 import java.io.IOException;
 import java.io.ObjectInputStream;
@@ -49,6 +50,8 @@
      */
     static final long serialVersionUID = -4116945555530446652L;
 
+    private static Logger log = Logger.getLogger(NodeState.class);
+
     /**
      * the name of this node's primary type
      */
@@ -87,8 +90,7 @@
     /**
      * Listeners (weak references)
      */
-    private final transient ReferenceMap listeners =
-            new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK);
+    private final transient Collection listeners = new WeakIdentityCollection(3);
 
     /**
      * Constructs a new node state that is initially connected to an overlayed
@@ -675,8 +677,12 @@
     public void addListener(ItemStateListener listener) {
         if (listener instanceof NodeStateListener) {
             synchronized (listeners) {
-                if (!listeners.containsKey(listener)) {
-                    listeners.put(listener, listener);
+                if (listeners.contains(listener)) {
+                    log.debug("listener already registered: " + listener);
+                    // no need to add to call ItemState.addListener()
+                    return;
+                } else {
+                    listeners.add(listener);
                 }
             }
         }
@@ -704,7 +710,7 @@
      */
     protected void notifyNodeAdded(ChildNodeEntry added) {
         synchronized (listeners) {
-            MapIterator iter = listeners.mapIterator();
+            Iterator iter = listeners.iterator();
             while (iter.hasNext()) {
                 NodeStateListener l = (NodeStateListener) iter.next();
                 if (l != null) {
@@ -720,7 +726,7 @@
      */
     protected void notifyNodesReplaced() {
         synchronized (listeners) {
-            MapIterator iter = listeners.mapIterator();
+            Iterator iter = listeners.iterator();
             while (iter.hasNext()) {
                 NodeStateListener l = (NodeStateListener) iter.next();
                 if (l != null) {
@@ -735,7 +741,7 @@
      */
     protected void notifyNodeRemoved(ChildNodeEntry removed) {
         synchronized (listeners) {
-            MapIterator iter = listeners.mapIterator();
+            Iterator iter = listeners.iterator();
             while (iter.hasNext()) {
                 NodeStateListener l = (NodeStateListener) iter.next();
                 if (l != null) {

Added: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/WeakIdentityCollection.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/WeakIdentityCollection.java?rev=378539&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/WeakIdentityCollection.java (added)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/WeakIdentityCollection.java Fri Feb 17 07:01:11 2006
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.util;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.BitSet;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.lang.ref.ReferenceQueue;
+
+/**
+ * <code>WeakIdentityCollection</code> implements a Collection with weak values.
+ * Equality of elements is tested using the == operator.
+ * <p/>
+ * This collection does not hide the fact that the garbage collector will remove
+ * a mapping at some point in time. Thus, the {@link java.util.Iterator} returned
+ * by this collection might return <code>null</code> values. The same applies
+ * to the method {@link #toArray()} in both its variants.
+ */
+public class WeakIdentityCollection implements Collection {
+
+    /**
+     * The weak references.
+     */
+    private transient WeakRef[] elementData;
+
+    /**
+     * The current number of elements in {@link #elementData}.
+     */
+    private int size;
+
+    /**
+     * The reference queue to poll for references that point to unreachable
+     * objects.
+     */
+    private final ReferenceQueue refQueue = new ReferenceQueue();
+
+    /**
+     * BitSet where a set bit indicates that the slot at this position in
+     * {@link #elementData} is empty and can be reused.
+     */
+    private final BitSet emptySlots = new BitSet();
+
+    /**
+     * Creates a new WeakIdentityCollection.
+     *
+     * @param initialCapacity the initial capacity.
+     */
+    public WeakIdentityCollection(int initialCapacity) {
+        if (initialCapacity < 0) {
+            throw new IllegalArgumentException("Illegal Capacity: " +
+                    initialCapacity);
+        }
+        this.elementData = new WeakRef[initialCapacity];
+    }
+
+    /**
+     * Returns the current size of this collection.
+     *
+     * @return the current size of this collection.
+     */
+    public int size() {
+        return size;
+    }
+
+    /**
+     * Returns <code>true</code> if this collection is empty.
+     *
+     * @return <code>true</code> if this collection is empty.
+     */
+    public boolean isEmpty() {
+        return size == 0;
+    }
+
+    /**
+     * Releases all references held by this collection.
+     */
+    public void clear() {
+        for (int i = 0; i < size; i++) {
+            elementData[i] = null;
+        }
+        size = 0;
+        emptySlots.clear();
+    }
+
+    /**
+     * Adds object <code>o</code> to this collection.
+     *
+     * @param o the object to add.
+     * @return always <code>true</code> as this collection allows duplicates.
+     * @throws NullPointerException if <code>o</code> is <code>null</code>.
+     */
+    public boolean add(Object o) {
+        if (o == null) {
+            throw new NullPointerException("Object must not be null");
+        }
+        // poll refQueue for a slot we can reuse
+        WeakRef ref = (WeakRef) refQueue.poll();
+        if (ref != null) {
+            elementData[ref.index] = new WeakRef(o, ref.index);
+            cleanQueue();
+        } else if (!emptySlots.isEmpty()) {
+            int idx = emptySlots.nextSetBit(0);
+            elementData[idx] = new WeakRef(o, idx);
+            emptySlots.clear(idx);
+        } else {
+            ensureCapacity(size + 1);
+            elementData[size++] = new WeakRef(o, size - 1);
+        }
+        return true;
+    }
+
+    /**
+     * Returns <code>true</code> if this collection contains <code>o</code>.
+     *
+     * @param o element whose presence in this collection is to be tested.
+     * @return <code>true</code> if this collection contains the specified
+     *         element
+     */
+    public boolean contains(Object o) {
+        for (int i = 0; i < size; i++) {
+            if (elementData[i].get() == o) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Removes the object <code>o</code> from this collection if it is present.
+     *
+     * @param o the object to remove.
+     * @return <code>true</code> if this collection changed as a result of the
+     *         call.
+     */
+    public boolean remove(Object o) {
+        for (int i = 0; i < size; i++) {
+            if (elementData[i].get() == o) {
+                emptySlots.set(i);
+                // overwrite entry with dummy ref
+                elementData[i] = new WeakRef(null, i);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @throws UnsupportedOperationException always.
+     */
+    public boolean addAll(Collection c) {
+        throw new UnsupportedOperationException("addAll");
+    }
+
+    /**
+     * @throws UnsupportedOperationException always.
+     */
+    public boolean containsAll(Collection c) {
+        throw new UnsupportedOperationException("containsAll");
+    }
+
+    /**
+     * @throws UnsupportedOperationException always.
+     */
+    public boolean removeAll(Collection c) {
+        throw new UnsupportedOperationException("removeAll");
+    }
+
+    /**
+     * @throws UnsupportedOperationException always.
+     */
+    public boolean retainAll(Collection c) {
+        throw new UnsupportedOperationException("retainAll");
+    }
+
+    /**
+     * Returns an {@link java.util.Iterator} over the elements of this
+     * collection. The returned iterator is not fail-fast. That is, it does
+     * not throw a {@link java.util.ConcurrentModificationException} if this
+     * collection is modified while iterating over the collection.
+     *
+     * @return an {@link java.util.Iterator} over the elements of this
+     *         collection.
+     */
+    public Iterator iterator() {
+        return new Iter();
+    }
+
+    /**
+     * Returns an array containing all of the elements in this collection. The
+     * returned array may contain <code>null</code> elements!
+     *
+     * @return an array containing all of the elements in this collection.
+     */
+    public Object[] toArray() {
+        Object[] result = new Object[size];
+        for (int i = 0; i < result.length; i++) {
+            result[i] = elementData[i].get();
+        }
+        return result;
+    }
+
+    /**
+     * The returned array may contain <code>null</code> elements!
+     * @inheritDoc
+     */
+    public Object[] toArray(Object a[]) {
+        if (a.length < size) {
+            a = (Object[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
+        }
+
+        for (int i = 0; i < size; i++) {
+            a[i] = elementData[i].get();
+        }
+
+        if (a.length > size) {
+            a[size] = null;
+        }
+
+        return a;
+    }
+
+    /**
+     * Ensures that the internal {@link #elementData} has
+     * <code>minCapacity</code>.
+     *
+     * @param minCapacity the minimal capacity to ensure.
+     */
+    private void ensureCapacity(int minCapacity) {
+        int oldCapacity = elementData.length;
+        if (minCapacity > oldCapacity) {
+            Object oldData[] = elementData;
+            int newCapacity = (oldCapacity * 3)/2 + 1;
+                if (newCapacity < minCapacity)
+            newCapacity = minCapacity;
+            elementData = new WeakRef[newCapacity];
+            System.arraycopy(oldData, 0, elementData, 0, size);
+        }
+    }
+
+    /**
+     * Polls the reference queue until no reference is available anymore.
+     */
+    private void cleanQueue() {
+        WeakRef ref;
+        while ((ref = (WeakRef) refQueue.poll()) != null) {
+            emptySlots.set(ref.index);
+        }
+    }
+
+    /**
+     * Iterator implementation for this collecation.
+     */
+    private final class Iter implements Iterator {
+
+        /**
+         * current index.
+         */
+        private int index;
+
+        /**
+         * The current element data.
+         */
+        private Reference[] elements = elementData;
+
+        /**
+         * The current size of this collection.
+         */
+        private int size = WeakIdentityCollection.this.size;
+
+        /**
+         * @throws UnsupportedOperationException always.
+         */
+        public void remove() {
+            throw new UnsupportedOperationException("remove");
+        }
+
+        /**
+         * @inheritDoc
+         */
+        public boolean hasNext() {
+            return index < size;
+        }
+
+        /**
+         * @inheritDoc
+         */ 
+        public Object next() {
+            if (index >= size) {
+                throw new NoSuchElementException();
+            }
+            return elements[index++].get();
+        }
+    }
+
+    /**
+     * Weak reference with index value that points to the slot in {@link
+     * WeakIdentityCollection#elementData}.
+     */
+    private final class WeakRef extends WeakReference {
+
+        /**
+         * The index of this weak reference.
+         */
+        private final int index;
+
+        /**
+         * Creates a new WeakRef.
+         *
+         * @param referent object the new weak reference will refer to.
+         * @param index    the index of this weak reference.
+         */
+        public WeakRef(Object referent, int index) {
+            super(referent, refQueue);
+            this.index = index;
+        }
+    }
+}

Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/WeakIdentityCollection.java
------------------------------------------------------------------------------
    svn:eol-style = native