You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by us...@apache.org on 2013/04/13 11:28:35 UTC
svn commit: r1467579 - in /lucene/dev/trunk/lucene: ./
core/src/java/org/apache/lucene/util/ core/src/test/org/apache/lucene/util/
Author: uschindler
Date: Sat Apr 13 09:28:35 2013
New Revision: 1467579
URL: http://svn.apache.org/r1467579
Log:
LUCENE-4930: Reduce contention in older/buggy JVMs when using AttributeSource#addAttribute() because java.lang.ref.ReferenceQueue#poll() is implemented using synchronization.
Modified:
lucene/dev/trunk/lucene/CHANGES.txt
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/AttributeSource.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/VirtualMethod.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/WeakIdentityMap.java
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/util/TestWeakIdentityMap.java
Modified: lucene/dev/trunk/lucene/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/CHANGES.txt?rev=1467579&r1=1467578&r2=1467579&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/CHANGES.txt (original)
+++ lucene/dev/trunk/lucene/CHANGES.txt Sat Apr 13 09:28:35 2013
@@ -216,6 +216,11 @@ Optimizations
* LUCENE-4926: Speed up DisjunctionMatchQuery. (Robert Muir)
+* LUCENE-4930: Reduce contention in older/buggy JVMs when using
+ AttributeSource#addAttribute() because java.lang.ref.ReferenceQueue#poll()
+ is implemented using synchronization. (Christian Ziech, Karl Wright,
+ Uwe Schindler)
+
API Changes
* LUCENE-4844: removed TaxonomyReader.getParent(), you should use
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/AttributeSource.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/AttributeSource.java?rev=1467579&r1=1467578&r2=1467579&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/AttributeSource.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/AttributeSource.java Sat Apr 13 09:28:35 2013
@@ -55,9 +55,9 @@ public class AttributeSource {
private static final class DefaultAttributeFactory extends AttributeFactory {
private static final WeakIdentityMap<Class<? extends Attribute>, WeakReference<Class<? extends AttributeImpl>>> attClassImplMap =
- WeakIdentityMap.newConcurrentHashMap();
+ WeakIdentityMap.newConcurrentHashMap(false);
- private DefaultAttributeFactory() {}
+ DefaultAttributeFactory() {}
@Override
public AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass) {
@@ -201,7 +201,7 @@ public class AttributeSource {
/** a cache that stores all interfaces for known implementation classes for performance (slow reflection) */
private static final WeakIdentityMap<Class<? extends AttributeImpl>,LinkedList<WeakReference<Class<? extends Attribute>>>> knownImplClasses =
- WeakIdentityMap.newConcurrentHashMap();
+ WeakIdentityMap.newConcurrentHashMap(false);
static LinkedList<WeakReference<Class<? extends Attribute>>> getAttributeInterfaces(final Class<? extends AttributeImpl> clazz) {
LinkedList<WeakReference<Class<? extends Attribute>>> foundInterfaces = knownImplClasses.get(clazz);
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/VirtualMethod.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/VirtualMethod.java?rev=1467579&r1=1467578&r2=1467579&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/VirtualMethod.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/VirtualMethod.java Sat Apr 13 09:28:35 2013
@@ -63,7 +63,7 @@ public final class VirtualMethod<C> {
private final Class<C> baseClass;
private final String method;
private final Class<?>[] parameters;
- private final WeakIdentityMap<Class<? extends C>, Integer> cache = WeakIdentityMap.newConcurrentHashMap();
+ private final WeakIdentityMap<Class<? extends C>, Integer> cache = WeakIdentityMap.newConcurrentHashMap(false);
/**
* Creates a new instance for the given {@code baseClass} and method declaration.
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/WeakIdentityMap.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/WeakIdentityMap.java?rev=1467579&r1=1467578&r2=1467579&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/WeakIdentityMap.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/WeakIdentityMap.java Sat Apr 13 09:28:35 2013
@@ -44,25 +44,65 @@ import java.util.concurrent.ConcurrentHa
* if not implemented carefully. The map only contains {@link Iterator} implementations
* on the values and not-GCed keys. Lucene's implementation also supports {@code null}
* keys, but those are never weak!
+ *
+ * <p><a name="reapInfo" />The map supports two modes of operation:
+ * <ul>
+ * <li>{@code reapOnRead = true}: This behaves identical to a {@link java.util.WeakHashMap}
+ * where it also cleans up the reference queue on every read operation ({@link #get(Object)},
+ * {@link #containsKey(Object)}, {@link #size()}, {@link #valueIterator()}), freeing map entries
+ * of already GCed keys.</li>
+ * <li>{@code reapOnRead = false}: This mode does not call {@link #reap()} on every read
+ * operation. In this case, the reference queue is only cleaned up on write operations
+ * (like {@link #put(Object, Object)}). This is ideal for maps with few entries where
+ * the keys are unlikely be garbage collected, but there are lots of {@link #get(Object)}
+ * operations. The code can still call {@link #reap()} to manually clean up the queue without
+ * doing a write operation.</li>
+ * </ul>
*
* @lucene.internal
*/
public final class WeakIdentityMap<K,V> {
private final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
private final Map<IdentityWeakReference, V> backingStore;
+ private final boolean reapOnRead;
- /** Creates a new {@code WeakIdentityMap} based on a non-synchronized {@link HashMap}. */
- public static final <K,V> WeakIdentityMap<K,V> newHashMap() {
- return new WeakIdentityMap<K,V>(new HashMap<IdentityWeakReference,V>());
+
+ /**
+ * Creates a new {@code WeakIdentityMap} based on a non-synchronized {@link HashMap}.
+ * The map <a href="#reapInfo">cleans up the reference queue on every read operation</a>.
+ */
+ public static <K,V> WeakIdentityMap<K,V> newHashMap() {
+ return newHashMap(true);
+ }
+
+ /**
+ * Creates a new {@code WeakIdentityMap} based on a non-synchronized {@link HashMap}.
+ * @param reapOnRead controls if the map <a href="#reapInfo">cleans up the reference queue on every read operation</a>.
+ */
+ public static <K,V> WeakIdentityMap<K,V> newHashMap(boolean reapOnRead) {
+ return new WeakIdentityMap<K,V>(new HashMap<IdentityWeakReference,V>(), reapOnRead);
+ }
+
+ /**
+ * Creates a new {@code WeakIdentityMap} based on a {@link ConcurrentHashMap}.
+ * The map <a href="#reapInfo">cleans up the reference queue on every read operation</a>.
+ */
+ public static <K,V> WeakIdentityMap<K,V> newConcurrentHashMap() {
+ return newConcurrentHashMap(true);
}
- /** Creates a new {@code WeakIdentityMap} based on a {@link ConcurrentHashMap}. */
- public static final <K,V> WeakIdentityMap<K,V> newConcurrentHashMap() {
- return new WeakIdentityMap<K,V>(new ConcurrentHashMap<IdentityWeakReference,V>());
+ /**
+ * Creates a new {@code WeakIdentityMap} based on a {@link ConcurrentHashMap}.
+ * @param reapOnRead controls if the map <a href="#reapInfo">cleans up the reference queue on every read operation</a>.
+ */
+ public static <K,V> WeakIdentityMap<K,V> newConcurrentHashMap(boolean reapOnRead) {
+ return new WeakIdentityMap<K,V>(new ConcurrentHashMap<IdentityWeakReference,V>(), reapOnRead);
}
- private WeakIdentityMap(Map<IdentityWeakReference, V> backingStore) {
+ /** Private only constructor, to create use the static factory methods. */
+ private WeakIdentityMap(Map<IdentityWeakReference, V> backingStore, boolean reapOnRead) {
this.backingStore = backingStore;
+ this.reapOnRead = reapOnRead;
}
/** Removes all of the mappings from this map. */
@@ -73,13 +113,13 @@ public final class WeakIdentityMap<K,V>
/** Returns {@code true} if this map contains a mapping for the specified key. */
public boolean containsKey(Object key) {
- reap();
+ if (reapOnRead) reap();
return backingStore.containsKey(new IdentityWeakReference(key, null));
}
/** Returns the value to which the specified key is mapped. */
public V get(Object key) {
- reap();
+ if (reapOnRead) reap();
return backingStore.get(new IdentityWeakReference(key, null));
}
@@ -113,7 +153,7 @@ public final class WeakIdentityMap<K,V>
public int size() {
if (backingStore.isEmpty())
return 0;
- reap();
+ if (reapOnRead) reap();
return backingStore.size();
}
@@ -178,13 +218,21 @@ public final class WeakIdentityMap<K,V>
/** Returns an iterator over all values of this map.
* This iterator may return values whose key is already
- * garbage collected while iterator is consumed. */
+ * garbage collected while iterator is consumed,
+ * especially if {@code reapOnRead} is {@code false}. */
public Iterator<V> valueIterator() {
- reap();
+ if (reapOnRead) reap();
return backingStore.values().iterator();
}
- private void reap() {
+ /**
+ * This method manually cleans up the reference queue to remove all garbage
+ * collected key/value pairs from the map. Calling this method is not needed
+ * if {@code reapOnRead = true}. Otherwise it might be a good idea
+ * to call this method when there is spare time (e.g. from a background thread).
+ * @see <a href="#reapInfo">Information about the <code>reapOnRead</code> setting</a>
+ */
+ public void reap() {
Reference<?> zombie;
while ((zombie = queue.poll()) != null) {
backingStore.remove(zombie);
Modified: lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/util/TestWeakIdentityMap.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/util/TestWeakIdentityMap.java?rev=1467579&r1=1467578&r2=1467579&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/util/TestWeakIdentityMap.java (original)
+++ lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/util/TestWeakIdentityMap.java Sat Apr 13 09:28:35 2013
@@ -18,7 +18,6 @@
package org.apache.lucene.util;
import java.util.Iterator;
-import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.concurrent.atomic.AtomicReferenceArray;
@@ -30,7 +29,7 @@ public class TestWeakIdentityMap extends
public void testSimpleHashMap() {
final WeakIdentityMap<String,String> map =
- WeakIdentityMap.newHashMap();
+ WeakIdentityMap.newHashMap(random().nextBoolean());
// we keep strong references to the keys,
// so WeakIdentityMap will not forget about them:
String key1 = new String("foo");
@@ -165,7 +164,7 @@ public class TestWeakIdentityMap extends
final int threadCount = 8, keyCount = 1024;
final ExecutorService exec = Executors.newFixedThreadPool(threadCount, new NamedThreadFactory("testConcurrentHashMap"));
final WeakIdentityMap<Object,Integer> map =
- WeakIdentityMap.newConcurrentHashMap();
+ WeakIdentityMap.newConcurrentHashMap(random().nextBoolean());
// we keep strong references to the keys,
// so WeakIdentityMap will not forget about them:
final AtomicReferenceArray<Object> keys = new AtomicReferenceArray<Object>(keyCount);