You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by ni...@apache.org on 2020/01/23 17:19:31 UTC

[lucenenet] 01/02: Factored out WeakIdentityMap in favor of ConditionalWeakTable (fixes LUCENENET-640, closes #236)

This is an automated email from the ASF dual-hosted git repository.

nightowl888 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/lucenenet.git

commit 7fd3de71d60a50059b6a8276bccb8d35fd6a1c83
Author: Shad Storhaug <sh...@shadstorhaug.com>
AuthorDate: Thu Jan 23 20:55:36 2020 +0700

    Factored out WeakIdentityMap in favor of ConditionalWeakTable (fixes LUCENENET-640, closes #236)
    
    Squashed commit of the following:
    
    commit 5303bdf7fbe196d219cfebef6f44bcc28a2692a0
    Author: Shad Storhaug <sh...@shadstorhaug.com>
    Date:   Tue Jan 21 16:22:44 2020 +0700
    
        Lucene.Net.Util.WeakIdentityMap: Factored out and replaced with ConditionalWeakTable<TKey, TValue> (fixes LUCENENET-640)
    
    commit 7d38f1fe6375a19a19fdf6c2d9dd25ebb8797e48
    Author: Shad Storhaug <sh...@shadstorhaug.com>
    Date:   Tue Jan 21 04:49:07 2020 +0700
    
        Lucene.Net.Util.AttributeSource: Removed lock for .NET Standard 2.1+, since we can use AddOrUpdate in the case a weak reference is no longer alive rather than using multiple calls to get the same effect.
    
    commit 387b9b27efbc530d6bfee984f5bc225cc768c558
    Author: Shad Storhaug <sh...@shadstorhaug.com>
    Date:   Tue Jan 21 00:19:54 2020 +0700
    
        Lucene.Net.Util.AttributeSource: Factored out WeakIdentityMap in favor of ConditionalWeakTable (see LUCENENET-640)
    
    commit d9fdf766cb5c2a77dd9a693386c41c9d5fc30dc1
    Author: Shad Storhaug <sh...@shadstorhaug.com>
    Date:   Mon Jan 20 22:37:18 2020 +0700
    
        Lucene.Net.Util.VirtualMethod: Factored out WeakIdenityMap in favor of ConditionalWeakTable (see LUCENENET-640)
---
 src/Lucene.Net.Tests/Util/TestWeakIdentityMap.cs | 715 ++++++++++----------
 src/Lucene.Net/Store/ByteBufferIndexInput.cs     |  37 +-
 src/Lucene.Net/Util/AttributeImpl.cs             |  11 +-
 src/Lucene.Net/Util/AttributeSource.cs           | 105 +--
 src/Lucene.Net/Util/VirtualMethod.cs             |  57 +-
 src/Lucene.Net/Util/WeakIdentityMap.cs           | 801 ++++++++++++-----------
 6 files changed, 909 insertions(+), 817 deletions(-)

diff --git a/src/Lucene.Net.Tests/Util/TestWeakIdentityMap.cs b/src/Lucene.Net.Tests/Util/TestWeakIdentityMap.cs
index 2c46d41..d98ec11 100644
--- a/src/Lucene.Net.Tests/Util/TestWeakIdentityMap.cs
+++ b/src/Lucene.Net.Tests/Util/TestWeakIdentityMap.cs
@@ -1,355 +1,360 @@
-using J2N.Threading;
-using J2N.Threading.Atomic;
-using Lucene.Net.Attributes;
-using NUnit.Framework;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Threading;
-
-namespace Lucene.Net.Util
-{
-    /*
-     * Licensed to the Apache Software Foundation (ASF) under one or more
-     * contributor license agreements.  See the NOTICE file distributed with
-     * this work for additional information regarding copyright ownership.
-     * The ASF licenses this file to You 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.
-     */
-
-    [TestFixture]
-    public class TestWeakIdentityMap : LuceneTestCase
-    {
-
-        [Test, LongRunningTest]
-        public virtual void TestSimpleHashMap()
-        {
-            WeakIdentityMap<string, string> map = WeakIdentityMap<string, string>.NewHashMap(Random.NextBoolean());
-            // we keep strong references to the keys,
-            // so WeakIdentityMap will not forget about them:
-            string key1 = "foo";
-            string key2 = "test1 foo".Split(' ')[1];
-            string key3 = "test2 foo".Split(' ')[1];
-
-            // LUCENENET NOTE: As per http://stackoverflow.com/a/543329/181087,
-            // the above hack is required in order to ensure the AreNotSame
-            // check will work. If you assign the same string to 3 different variables
-            // without doing some kind of manipulation from the original string, the
-            // AreNotSame test will fail because the references will be the same.
-
-            Assert.AreNotSame(key1, key2);
-            Assert.AreEqual(key1, key2);
-            Assert.AreNotSame(key1, key3);
-            Assert.AreEqual(key1, key3);
-            Assert.AreNotSame(key2, key3);
-            Assert.AreEqual(key2, key3);
-
-            // try null key & check its iterator also return null:
-            map.Put(null, "null");
-            {
-                using (IEnumerator<string> iter = map.Keys.GetEnumerator())
-                {
-                    Assert.IsTrue(iter.MoveNext());
-                    Assert.IsNull(iter.Current);
-                    Assert.IsFalse(iter.MoveNext());
-                    Assert.IsFalse(iter.MoveNext());
-                }
-            }
-            // 2 more keys:
-            map.Put(key1, "bar1");
-            map.Put(key2, "bar2");
-
-            Assert.AreEqual(3, map.Count);
-
-            Assert.AreEqual("bar1", map.Get(key1));
-            Assert.AreEqual("bar2", map.Get(key2));
-            Assert.AreEqual(null, map.Get(key3));
-            Assert.AreEqual("null", map.Get(null));
-
-            Assert.IsTrue(map.ContainsKey(key1));
-            Assert.IsTrue(map.ContainsKey(key2));
-            Assert.IsFalse(map.ContainsKey(key3));
-            Assert.IsTrue(map.ContainsKey(null));
-
-            // repeat and check that we have no double entries
-            map.Put(key1, "bar1");
-            map.Put(key2, "bar2");
-            map.Put(null, "null");
-
-            Assert.AreEqual(3, map.Count);
-
-            Assert.AreEqual("bar1", map.Get(key1));
-            Assert.AreEqual("bar2", map.Get(key2));
-            Assert.AreEqual(null, map.Get(key3));
-            Assert.AreEqual("null", map.Get(null));
-
-            Assert.IsTrue(map.ContainsKey(key1));
-            Assert.IsTrue(map.ContainsKey(key2));
-            Assert.IsFalse(map.ContainsKey(key3));
-            Assert.IsTrue(map.ContainsKey(null));
-
-            map.Remove(null);
-            Assert.AreEqual(2, map.Count);
-            map.Remove(key1);
-            Assert.AreEqual(1, map.Count);
-            map.Put(key1, "bar1");
-            map.Put(key2, "bar2");
-            map.Put(key3, "bar3");
-            Assert.AreEqual(3, map.Count);
-
-            int c = 0, keysAssigned = 0;
-            foreach (object k in map.Keys)
-            {
-                //Assert.IsTrue(iter.hasNext()); // try again, should return same result!
-                // LUCENENET NOTE: Need object.ReferenceEquals here because the == operator does more than check reference equality
-                Assert.IsTrue(object.ReferenceEquals(k, key1) || object.ReferenceEquals(k, key2) | object.ReferenceEquals(k, key3));
-                keysAssigned += object.ReferenceEquals(k, key1) ? 1 : (object.ReferenceEquals(k, key2) ? 2 : 4);
-                c++;
-            }
-            Assert.AreEqual(3, c);
-            Assert.AreEqual(1 + 2 + 4, keysAssigned, "all keys must have been seen");
-
-            c = 0;
-            for (IEnumerator<string> iter = map.Values.GetEnumerator(); iter.MoveNext();)
-            {
-                string v = iter.Current;
-                Assert.IsTrue(v.StartsWith("bar", StringComparison.Ordinal));
-                c++;
-            }
-            Assert.AreEqual(3, c);
-
-            // clear strong refs
-            key1 = key2 = key3 = null;
-
-            // check that GC does not cause problems in reap() method, wait 1 second and let GC work:
-            int size = map.Count;
-            for (int i = 0; size > 0 && i < 10; i++)
-            {
-#if !NETSTANDARD1_6
-                try
-                {
-#endif
-                    GC.Collect();
-                    int newSize = map.Count;
-                    Assert.IsTrue(size >= newSize, "previousSize(" + size + ")>=newSize(" + newSize + ")");
-                    size = newSize;
-                    Thread.Sleep(TimeSpan.FromSeconds(1));
-                    c = 0;
-                    foreach (object k in map.Keys)
-                    {
-                        Assert.IsNotNull(k);
-                        c++;
-                    }
-                    newSize = map.Count;
-                    Assert.IsTrue(size >= c, "previousSize(" + size + ")>=iteratorSize(" + c + ")");
-                    Assert.IsTrue(c >= newSize, "iteratorSize(" + c + ")>=newSize(" + newSize + ")");
-                    size = newSize;
-#if !NETSTANDARD1_6
-                }
-#pragma warning disable 168
-                catch (ThreadInterruptedException ie)
-#pragma warning restore 168
-                {
-                }
-#endif
-            }
-
-            map.Clear();
-            Assert.AreEqual(0, map.Count);
-            Assert.IsTrue(map.IsEmpty);
-
-            using (IEnumerator<string> it = map.Keys.GetEnumerator())
-                Assert.IsFalse(it.MoveNext());
-            /*try
-            {
-              it.Next();
-              Assert.Fail("Should throw NoSuchElementException");
-            }
-            catch (NoSuchElementException nse)
-            {
-            }*/
-
-            // LUCENENET NOTE: As per http://stackoverflow.com/a/543329/181087,
-            // the following hack is required in order to ensure the string references
-            // are different. If you assign the same string to 2 different variables
-            // without doing some kind of manipulation from the original string, the
-            // references will be the same.
-
-            key1 = "test3 foo".Split(' ')[1];
-            key2 = "test4 foo".Split(' ')[1];
-            map.Put(key1, "bar1");
-            map.Put(key2, "bar2");
-            Assert.AreEqual(2, map.Count);
-
-            map.Clear();
-            Assert.AreEqual(0, map.Count);
-            Assert.IsTrue(map.IsEmpty);
-        }
-
-        [Test, LongRunningTest]
-        public virtual void TestConcurrentHashMap()
-        {
-            // don't make threadCount and keyCount random, otherwise easily OOMs or fails otherwise:
-            const int threadCount = 8, keyCount = 1024;
-
-            RunnableAnonymousInnerClassHelper[] workers = new RunnableAnonymousInnerClassHelper[threadCount];
-            WeakIdentityMap<object, int?> map = WeakIdentityMap<object, int?>.NewConcurrentHashMap(Random.NextBoolean());
-            // we keep strong references to the keys,
-            // so WeakIdentityMap will not forget about them:
-            AtomicReferenceArray<object> keys = new AtomicReferenceArray<object>(keyCount);
-            for (int j = 0; j < keyCount; j++)
-            {
-                keys[j] = new object();
-            }
-
-            try
-            {
-                for (int t = 0; t < threadCount; t++)
-                {
-                    Random rnd = new Random(Random.Next());
-                    var worker = new RunnableAnonymousInnerClassHelper(this, keyCount, map, keys, rnd);
-                    workers[t] = worker;
-                    worker.Start();
-                }
-            }
-            finally
-            {
-                foreach (var w in workers)
-                {
-                    w.Join();
-                }
-            }
-
-            // LUCENENET: Since assertions were done on the other threads, we need to check the
-            // results here.
-            for (int i = 0; i < workers.Length; i++)
-            {
-                assertTrue(string.Format(CultureInfo.InvariantCulture,
-                    "worker thread {0} of {1} failed \n" + workers[i].Error, i, workers.Length),
-                    workers[i].Error == null);
-            }
-
-
-            // clear strong refs
-            for (int j = 0; j < keyCount; j++)
-            {
-                keys[j] = null;
-            }
-
-            // check that GC does not cause problems in reap() method:
-            int size = map.Count;
-            for (int i = 0; size > 0 && i < 10; i++)
-            {
-#if !NETSTANDARD1_6
-                try
-                {
-#endif
-                    GC.Collect();
-                    int newSize = map.Count;
-                    Assert.IsTrue(size >= newSize, "previousSize(" + size + ")>=newSize(" + newSize + ")");
-                    size = newSize;
-                    Thread.Sleep(new TimeSpan(100L));
-                    int c = 0;
-                    foreach (object k in map.Keys)
-                    {
-                        Assert.IsNotNull(k);
-                        c++;
-                    }
-                    newSize = map.Count;
-                    Assert.IsTrue(size >= c, "previousSize(" + size + ")>=iteratorSize(" + c + ")");
-                    Assert.IsTrue(c >= newSize, "iteratorSize(" + c + ")>=newSize(" + newSize + ")");
-                    size = newSize;
-#if !NETSTANDARD1_6
-                }
-#pragma warning disable 168
-                catch (ThreadInterruptedException ie)
-#pragma warning restore 168
-                {
-                }
-#endif
-            }
-        }
-
-        private class RunnableAnonymousInnerClassHelper : ThreadJob
-        {
-            private readonly TestWeakIdentityMap outerInstance;
-
-            private readonly int keyCount;
-            private readonly WeakIdentityMap<object, int?> map;
-            private AtomicReferenceArray<object> keys;
-            private readonly Random rnd;
-            private volatile Exception error;
-
-            public RunnableAnonymousInnerClassHelper(TestWeakIdentityMap outerInstance, int keyCount, WeakIdentityMap<object, int?> map, AtomicReferenceArray<object> keys, Random rnd)
-            {
-                this.outerInstance = outerInstance;
-                this.keyCount = keyCount;
-                this.map = map;
-                this.keys = keys;
-                this.rnd = rnd;
-            }
-
-            public Exception Error
-            {
-                get { return error; }
-            }
-
-
-            public override void Run()
-            {
-                int count = AtLeast(rnd, 10000);
-                try
-                {
-                    for (int i = 0; i < count; i++)
-                    {
-                        int j = rnd.Next(keyCount);
-                        switch (rnd.Next(5))
-                        {
-                            case 0:
-                                map.Put(keys[j], Convert.ToInt32(j));
-                                break;
-                            case 1:
-                                int? v = map.Get(keys[j]);
-                                if (v != null)
-                                {
-                                    Assert.AreEqual(j, (int)v);
-                                }
-                                break;
-                            case 2:
-                                map.Remove(keys[j]);
-                                break;
-                            case 3:
-                                // renew key, the old one will be GCed at some time:
-                                keys[j] = new object();
-                                break;
-                            case 4:
-                                // check iterator still working
-                                foreach (object k in map.Keys)
-                                {
-                                    Assert.IsNotNull(k);
-                                }
-                                break;
-                            default:
-                                Assert.Fail("Should not get here.");
-                                break;
-                        }
-                    }
-                }
-                catch (Exception e)
-                {
-                    e.printStackTrace();
-                    this.error = e;
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
+// LUCENENET specific - factored out this WeakIdentityMap<TKey, TValue> and replaced it with ConditionalWeakTable<TKey, TValue>.
+// ConditionalWeakTable<TKey, TValue> is thread-safe and internally uses RuntimeHelpers.GetHashCode()
+// to lookup the key, so it can be used as a direct replacement for WeakIdentityMap<TKey, TValue>
+// in most cases.
+
+//using J2N.Threading;
+//using J2N.Threading.Atomic;
+//using Lucene.Net.Attributes;
+//using NUnit.Framework;
+//using System;
+//using System.Collections.Generic;
+//using System.Globalization;
+//using System.Threading;
+
+//namespace Lucene.Net.Util
+//{
+//    /*
+//     * Licensed to the Apache Software Foundation (ASF) under one or more
+//     * contributor license agreements.  See the NOTICE file distributed with
+//     * this work for additional information regarding copyright ownership.
+//     * The ASF licenses this file to You 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.
+//     */
+
+//    [TestFixture]
+//    public class TestWeakIdentityMap : LuceneTestCase
+//    {
+
+//        [Test, LongRunningTest]
+//        public virtual void TestSimpleHashMap()
+//        {
+//            WeakIdentityMap<string, string> map = WeakIdentityMap<string, string>.NewHashMap(Random.NextBoolean());
+//            // we keep strong references to the keys,
+//            // so WeakIdentityMap will not forget about them:
+//            string key1 = "foo";
+//            string key2 = "test1 foo".Split(' ')[1];
+//            string key3 = "test2 foo".Split(' ')[1];
+
+//            // LUCENENET NOTE: As per http://stackoverflow.com/a/543329/181087,
+//            // the above hack is required in order to ensure the AreNotSame
+//            // check will work. If you assign the same string to 3 different variables
+//            // without doing some kind of manipulation from the original string, the
+//            // AreNotSame test will fail because the references will be the same.
+
+//            Assert.AreNotSame(key1, key2);
+//            Assert.AreEqual(key1, key2);
+//            Assert.AreNotSame(key1, key3);
+//            Assert.AreEqual(key1, key3);
+//            Assert.AreNotSame(key2, key3);
+//            Assert.AreEqual(key2, key3);
+
+//            // try null key & check its iterator also return null:
+//            map.Put(null, "null");
+//            {
+//                using (IEnumerator<string> iter = map.Keys.GetEnumerator())
+//                {
+//                    Assert.IsTrue(iter.MoveNext());
+//                    Assert.IsNull(iter.Current);
+//                    Assert.IsFalse(iter.MoveNext());
+//                    Assert.IsFalse(iter.MoveNext());
+//                }
+//            }
+//            // 2 more keys:
+//            map.Put(key1, "bar1");
+//            map.Put(key2, "bar2");
+
+//            Assert.AreEqual(3, map.Count);
+
+//            Assert.AreEqual("bar1", map.Get(key1));
+//            Assert.AreEqual("bar2", map.Get(key2));
+//            Assert.AreEqual(null, map.Get(key3));
+//            Assert.AreEqual("null", map.Get(null));
+
+//            Assert.IsTrue(map.ContainsKey(key1));
+//            Assert.IsTrue(map.ContainsKey(key2));
+//            Assert.IsFalse(map.ContainsKey(key3));
+//            Assert.IsTrue(map.ContainsKey(null));
+
+//            // repeat and check that we have no double entries
+//            map.Put(key1, "bar1");
+//            map.Put(key2, "bar2");
+//            map.Put(null, "null");
+
+//            Assert.AreEqual(3, map.Count);
+
+//            Assert.AreEqual("bar1", map.Get(key1));
+//            Assert.AreEqual("bar2", map.Get(key2));
+//            Assert.AreEqual(null, map.Get(key3));
+//            Assert.AreEqual("null", map.Get(null));
+
+//            Assert.IsTrue(map.ContainsKey(key1));
+//            Assert.IsTrue(map.ContainsKey(key2));
+//            Assert.IsFalse(map.ContainsKey(key3));
+//            Assert.IsTrue(map.ContainsKey(null));
+
+//            map.Remove(null);
+//            Assert.AreEqual(2, map.Count);
+//            map.Remove(key1);
+//            Assert.AreEqual(1, map.Count);
+//            map.Put(key1, "bar1");
+//            map.Put(key2, "bar2");
+//            map.Put(key3, "bar3");
+//            Assert.AreEqual(3, map.Count);
+
+//            int c = 0, keysAssigned = 0;
+//            foreach (object k in map.Keys)
+//            {
+//                //Assert.IsTrue(iter.hasNext()); // try again, should return same result!
+//                // LUCENENET NOTE: Need object.ReferenceEquals here because the == operator does more than check reference equality
+//                Assert.IsTrue(object.ReferenceEquals(k, key1) || object.ReferenceEquals(k, key2) | object.ReferenceEquals(k, key3));
+//                keysAssigned += object.ReferenceEquals(k, key1) ? 1 : (object.ReferenceEquals(k, key2) ? 2 : 4);
+//                c++;
+//            }
+//            Assert.AreEqual(3, c);
+//            Assert.AreEqual(1 + 2 + 4, keysAssigned, "all keys must have been seen");
+
+//            c = 0;
+//            for (IEnumerator<string> iter = map.Values.GetEnumerator(); iter.MoveNext();)
+//            {
+//                string v = iter.Current;
+//                Assert.IsTrue(v.StartsWith("bar", StringComparison.Ordinal));
+//                c++;
+//            }
+//            Assert.AreEqual(3, c);
+
+//            // clear strong refs
+//            key1 = key2 = key3 = null;
+
+//            // check that GC does not cause problems in reap() method, wait 1 second and let GC work:
+//            int size = map.Count;
+//            for (int i = 0; size > 0 && i < 10; i++)
+//            {
+//#if !NETSTANDARD1_6
+//                try
+//                {
+//#endif
+//                    GC.Collect();
+//                    int newSize = map.Count;
+//                    Assert.IsTrue(size >= newSize, "previousSize(" + size + ")>=newSize(" + newSize + ")");
+//                    size = newSize;
+//                    Thread.Sleep(TimeSpan.FromSeconds(1));
+//                    c = 0;
+//                    foreach (object k in map.Keys)
+//                    {
+//                        Assert.IsNotNull(k);
+//                        c++;
+//                    }
+//                    newSize = map.Count;
+//                    Assert.IsTrue(size >= c, "previousSize(" + size + ")>=iteratorSize(" + c + ")");
+//                    Assert.IsTrue(c >= newSize, "iteratorSize(" + c + ")>=newSize(" + newSize + ")");
+//                    size = newSize;
+//#if !NETSTANDARD1_6
+//                }
+//#pragma warning disable 168
+//                catch (ThreadInterruptedException ie)
+//#pragma warning restore 168
+//                {
+//                }
+//#endif
+//            }
+
+//            map.Clear();
+//            Assert.AreEqual(0, map.Count);
+//            Assert.IsTrue(map.IsEmpty);
+
+//            using (IEnumerator<string> it = map.Keys.GetEnumerator())
+//                Assert.IsFalse(it.MoveNext());
+//            /*try
+//            {
+//              it.Next();
+//              Assert.Fail("Should throw NoSuchElementException");
+//            }
+//            catch (NoSuchElementException nse)
+//            {
+//            }*/
+
+//            // LUCENENET NOTE: As per http://stackoverflow.com/a/543329/181087,
+//            // the following hack is required in order to ensure the string references
+//            // are different. If you assign the same string to 2 different variables
+//            // without doing some kind of manipulation from the original string, the
+//            // references will be the same.
+
+//            key1 = "test3 foo".Split(' ')[1];
+//            key2 = "test4 foo".Split(' ')[1];
+//            map.Put(key1, "bar1");
+//            map.Put(key2, "bar2");
+//            Assert.AreEqual(2, map.Count);
+
+//            map.Clear();
+//            Assert.AreEqual(0, map.Count);
+//            Assert.IsTrue(map.IsEmpty);
+//        }
+
+//        [Test, LongRunningTest]
+//        public virtual void TestConcurrentHashMap()
+//        {
+//            // don't make threadCount and keyCount random, otherwise easily OOMs or fails otherwise:
+//            const int threadCount = 8, keyCount = 1024;
+
+//            RunnableAnonymousInnerClassHelper[] workers = new RunnableAnonymousInnerClassHelper[threadCount];
+//            WeakIdentityMap<object, int?> map = WeakIdentityMap<object, int?>.NewConcurrentHashMap(Random.NextBoolean());
+//            // we keep strong references to the keys,
+//            // so WeakIdentityMap will not forget about them:
+//            AtomicReferenceArray<object> keys = new AtomicReferenceArray<object>(keyCount);
+//            for (int j = 0; j < keyCount; j++)
+//            {
+//                keys[j] = new object();
+//            }
+
+//            try
+//            {
+//                for (int t = 0; t < threadCount; t++)
+//                {
+//                    Random rnd = new Random(Random.Next());
+//                    var worker = new RunnableAnonymousInnerClassHelper(this, keyCount, map, keys, rnd);
+//                    workers[t] = worker;
+//                    worker.Start();
+//                }
+//            }
+//            finally
+//            {
+//                foreach (var w in workers)
+//                {
+//                    w.Join();
+//                }
+//            }
+
+//            // LUCENENET: Since assertions were done on the other threads, we need to check the
+//            // results here.
+//            for (int i = 0; i < workers.Length; i++)
+//            {
+//                assertTrue(string.Format(CultureInfo.InvariantCulture,
+//                    "worker thread {0} of {1} failed \n" + workers[i].Error, i, workers.Length),
+//                    workers[i].Error == null);
+//            }
+
+
+//            // clear strong refs
+//            for (int j = 0; j < keyCount; j++)
+//            {
+//                keys[j] = null;
+//            }
+
+//            // check that GC does not cause problems in reap() method:
+//            int size = map.Count;
+//            for (int i = 0; size > 0 && i < 10; i++)
+//            {
+//#if !NETSTANDARD1_6
+//                try
+//                {
+//#endif
+//                    GC.Collect();
+//                    int newSize = map.Count;
+//                    Assert.IsTrue(size >= newSize, "previousSize(" + size + ")>=newSize(" + newSize + ")");
+//                    size = newSize;
+//                    Thread.Sleep(new TimeSpan(100L));
+//                    int c = 0;
+//                    foreach (object k in map.Keys)
+//                    {
+//                        Assert.IsNotNull(k);
+//                        c++;
+//                    }
+//                    newSize = map.Count;
+//                    Assert.IsTrue(size >= c, "previousSize(" + size + ")>=iteratorSize(" + c + ")");
+//                    Assert.IsTrue(c >= newSize, "iteratorSize(" + c + ")>=newSize(" + newSize + ")");
+//                    size = newSize;
+//#if !NETSTANDARD1_6
+//                }
+//#pragma warning disable 168
+//                catch (ThreadInterruptedException ie)
+//#pragma warning restore 168
+//                {
+//                }
+//#endif
+//            }
+//        }
+
+//        private class RunnableAnonymousInnerClassHelper : ThreadJob
+//        {
+//            private readonly TestWeakIdentityMap outerInstance;
+
+//            private readonly int keyCount;
+//            private readonly WeakIdentityMap<object, int?> map;
+//            private AtomicReferenceArray<object> keys;
+//            private readonly Random rnd;
+//            private volatile Exception error;
+
+//            public RunnableAnonymousInnerClassHelper(TestWeakIdentityMap outerInstance, int keyCount, WeakIdentityMap<object, int?> map, AtomicReferenceArray<object> keys, Random rnd)
+//            {
+//                this.outerInstance = outerInstance;
+//                this.keyCount = keyCount;
+//                this.map = map;
+//                this.keys = keys;
+//                this.rnd = rnd;
+//            }
+
+//            public Exception Error
+//            {
+//                get { return error; }
+//            }
+
+
+//            public override void Run()
+//            {
+//                int count = AtLeast(rnd, 10000);
+//                try
+//                {
+//                    for (int i = 0; i < count; i++)
+//                    {
+//                        int j = rnd.Next(keyCount);
+//                        switch (rnd.Next(5))
+//                        {
+//                            case 0:
+//                                map.Put(keys[j], Convert.ToInt32(j));
+//                                break;
+//                            case 1:
+//                                int? v = map.Get(keys[j]);
+//                                if (v != null)
+//                                {
+//                                    Assert.AreEqual(j, (int)v);
+//                                }
+//                                break;
+//                            case 2:
+//                                map.Remove(keys[j]);
+//                                break;
+//                            case 3:
+//                                // renew key, the old one will be GCed at some time:
+//                                keys[j] = new object();
+//                                break;
+//                            case 4:
+//                                // check iterator still working
+//                                foreach (object k in map.Keys)
+//                                {
+//                                    Assert.IsNotNull(k);
+//                                }
+//                                break;
+//                            default:
+//                                Assert.Fail("Should not get here.");
+//                                break;
+//                        }
+//                    }
+//                }
+//                catch (Exception e)
+//                {
+//                    e.printStackTrace();
+//                    this.error = e;
+//                }
+//            }
+//        }
+//    }
+//}
\ No newline at end of file
diff --git a/src/Lucene.Net/Store/ByteBufferIndexInput.cs b/src/Lucene.Net/Store/ByteBufferIndexInput.cs
index 480359a..341d370 100644
--- a/src/Lucene.Net/Store/ByteBufferIndexInput.cs
+++ b/src/Lucene.Net/Store/ByteBufferIndexInput.cs
@@ -4,6 +4,7 @@ using System;
 using System.Diagnostics;
 using System.IO;
 using System.Reflection;
+using System.Runtime.CompilerServices;
 
 namespace Lucene.Net.Store
 {
@@ -50,17 +51,29 @@ namespace Lucene.Net.Store
 
         private ByteBuffer curBuf; // redundant for speed: buffers[curBufIndex]
         private bool isClone = false;
-        private readonly WeakIdentityMap<ByteBufferIndexInput, BoolRefWrapper> clones;
+        // LUCENENET: Using ConditionalWeakTable rather than WeakIdenityMap. ConditionalWeakTable
+        // uses RuntimeHelpers.GetHashCode() to find the item, so technically, it IS an identity collection.
+        private ConditionalWeakTable<ByteBufferIndexInput, BoolRefWrapper> clones;
 
         private class BoolRefWrapper
         {
+            private bool value;
+
             // .NET port: this is needed as bool is not a reference type
             public BoolRefWrapper(bool value)
             {
-                this.Value = value;
+                this.value = value;
+            }
+
+            public static implicit operator bool(BoolRefWrapper value)
+            {
+                return value.value;
             }
 
-            public bool Value { get; private set; }
+            public static implicit operator BoolRefWrapper(bool value)
+            {
+                return new BoolRefWrapper(value);
+            }
         }
 
         internal ByteBufferIndexInput(string resourceDescription, ByteBuffer[] buffers, long length, int chunkSizePower, bool trackClones)
@@ -70,7 +83,9 @@ namespace Lucene.Net.Store
             this.length = length;
             this.chunkSizePower = chunkSizePower;
             this.chunkSizeMask = (1L << chunkSizePower) - 1L;
-            this.clones = trackClones ? WeakIdentityMap<ByteBufferIndexInput, BoolRefWrapper>.NewConcurrentHashMap() : null;
+            // LUCENENET: Using ConditionalWeakTable rather than WeakIdenityMap. ConditionalWeakTable
+            // uses RuntimeHelpers.GetHashCode() to find the item, so technically, it IS an identity collection.
+            this.clones = trackClones ? new ConditionalWeakTable<ByteBufferIndexInput, BoolRefWrapper>() : null;
 
             Debug.Assert(chunkSizePower >= 0 && chunkSizePower <= 30);
             Debug.Assert(((long)((ulong)length >> chunkSizePower)) < int.MaxValue);
@@ -298,7 +313,7 @@ namespace Lucene.Net.Store
             // register the new clone in our clone list to clean it up on closing:
             if (clones != null)
             {
-                this.clones.Put(clone, new BoolRefWrapper(true));
+                this.clones.Add(clone, true);
             }
 
             return clone;
@@ -374,16 +389,22 @@ namespace Lucene.Net.Store
                     // for extra safety unset also all clones' buffers:
                     if (clones != null)
                     {
-                        foreach (ByteBufferIndexInput clone in clones.Keys)
+                        // LUCENENET: Since .NET will GC types that go out of scope automatically,
+                        // this isn't strictly necessary. However, we are doing it anyway when
+                        // the enumerator is available (.NET Standard 2.1+)
+#if FEATURE_CONDITIONALWEAKTABLE_ENUMERATOR
+                        foreach (var pair in clones)
                         {
-                            clone.UnsetBuffers();
+                            pair.Key.UnsetBuffers();
                         }
                         this.clones.Clear();
+#endif
+                        this.clones = null; // LUCENENET: de-reference the table so it can be GC'd
                     }
 
                     foreach (ByteBuffer b in bufs)
                     {
-                        FreeBuffer(b);
+                        FreeBuffer(b); // LUCENENET: This calls Dispose() when necessary
                     }
                 }
                 finally
diff --git a/src/Lucene.Net/Util/AttributeImpl.cs b/src/Lucene.Net/Util/AttributeImpl.cs
index d52147a..8df9683 100644
--- a/src/Lucene.Net/Util/AttributeImpl.cs
+++ b/src/Lucene.Net/Util/AttributeImpl.cs
@@ -119,21 +119,14 @@ namespace Lucene.Net.Util
         public virtual void ReflectWith(IAttributeReflector reflector) // LUCENENET NOTE: This method was abstract in Lucene
         {
             Type clazz = this.GetType();
-            LinkedList<WeakReference> interfaces = AttributeSource.GetAttributeInterfaces(clazz);
+            LinkedList<WeakReference<Type>> interfaces = AttributeSource.GetAttributeInterfaces(clazz);
 
             if (interfaces.Count != 1)
             {
                 throw new NotSupportedException(clazz.Name + " implements more than one Attribute interface, the default ReflectWith() implementation cannot handle this.");
             }
 
-            Type interf = (System.Type)interfaces.First().Target;
-
-            /*object target = interfaces.First.Value;
-
-            if (target == null)
-                return;
-
-            Type interf = target.GetType();// as Type;*/
+            interfaces.First.Value.TryGetTarget(out Type interf);
 
             //problem: the interfaces list has weak references that could have expired already
 
diff --git a/src/Lucene.Net/Util/AttributeSource.cs b/src/Lucene.Net/Util/AttributeSource.cs
index 7b46b88..3c9b80b 100644
--- a/src/Lucene.Net/Util/AttributeSource.cs
+++ b/src/Lucene.Net/Util/AttributeSource.cs
@@ -55,8 +55,10 @@ namespace Lucene.Net.Util
 
             private sealed class DefaultAttributeFactory : AttributeFactory
             {
-                internal static readonly WeakIdentityMap<Type, WeakReference> attClassImplMap = 
-                    WeakIdentityMap<Type, WeakReference>.NewConcurrentHashMap(false);
+                // LUCENENET: Using ConditionalWeakTable instead of WeakIdentityMap. A Type IS an
+                // identity for a class, so there is no need for an identity wrapper for it.
+                private static readonly ConditionalWeakTable<Type, WeakReference<Type>> attClassImplMap =
+                    new ConditionalWeakTable<Type, WeakReference<Type>>();
 
                 internal DefaultAttributeFactory()
                 {
@@ -77,23 +79,51 @@ namespace Lucene.Net.Util
                 internal static Type GetClassForInterface<T>() where T : IAttribute
                 {
                     var attClass = typeof(T);
-                    WeakReference @ref = attClassImplMap.Get(attClass);
-                    Type clazz = (@ref == null) ? null : (Type)@ref.Target;
-                    if (clazz == null)
+                    Type clazz;
+
+#if !FEATURE_CONDITIONALWEAKTABLE_ADDORUPDATE
+                    // LUCENENET: If the weakreference is dead, we need to explicitly remove and re-add its key.
+                    // We synchronize on attClassImplMap only to make the operation atomic. This does not actually
+                    // utilize the same lock as attClassImplMap does internally, but since this is the only place
+                    // it is used, it is fine here.
+
+                    // In .NET Standard 2.1, we can use AddOrUpdate, so don't need the lock.
+                    lock (attClassImplMap)
+#endif
                     {
-                        // we have the slight chance that another thread may do the same, but who cares?
-                        try
+                        var @ref = attClassImplMap.GetValue(attClass, (key) =>
                         {
-                            string name = attClass.FullName.Replace(attClass.Name, attClass.Name.Substring(1)) + ", " + attClass.GetTypeInfo().Assembly.FullName;
-                            attClassImplMap.Put(attClass, new WeakReference(clazz = Type.GetType(name, true)));
-                        }
-                        catch (Exception e)
+                            return CreateAttributeWeakReference(key, out clazz);
+                        });
+
+                        if (!@ref.TryGetTarget(out clazz))
                         {
-                            throw new System.ArgumentException("Could not find implementing class for " + attClass.Name, e);
+#if FEATURE_CONDITIONALWEAKTABLE_ADDORUPDATE
+                            // There is a small chance that multiple threads will get through here, but it doesn't matter
+                            attClassImplMap.AddOrUpdate(attClass, CreateAttributeWeakReference(attClass, out clazz));
+#else
+                            attClassImplMap.Remove(attClass);
+                            attClassImplMap.Add(attClass, CreateAttributeWeakReference(attClass, out clazz));
+#endif
                         }
                     }
+
                     return clazz;
                 }
+
+                // LUCENENET specific - factored this out so we can reuse
+                private static WeakReference<Type> CreateAttributeWeakReference(Type attClass, out Type clazz)
+                {
+                    try
+                    {
+                        string name = attClass.FullName.Replace(attClass.Name, attClass.Name.Substring(1)) + ", " + attClass.GetTypeInfo().Assembly.FullName;
+                        return new WeakReference<Type>(clazz = Type.GetType(name, true));
+                    }
+                    catch (Exception e)
+                    {
+                        throw new ArgumentException("Could not find implementing class for " + attClass.Name, e);
+                    }
+                }
             }
         }
 
@@ -147,7 +177,7 @@ namespace Lucene.Net.Util
         {
             if (input == null)
             {
-                throw new System.ArgumentException("input AttributeSource must not be null");
+                throw new ArgumentException("input AttributeSource must not be null");
             }
             this.attributes = input.attributes;
             this.attributeImpls = input.attributeImpls;
@@ -258,36 +288,33 @@ namespace Lucene.Net.Util
 
         /// <summary>
         /// A cache that stores all interfaces for known implementation classes for performance (slow reflection) </summary>
-        private static readonly WeakIdentityMap<Type, LinkedList<WeakReference>> knownImplClasses =
-            WeakIdentityMap<Type, LinkedList<WeakReference>>.NewConcurrentHashMap(false);
+        // LUCENENET: Using ConditionalWeakTable instead of WeakIdentityMap. A Type IS an
+        // identity for a class, so there is no need for an identity wrapper for it.
+        private static readonly ConditionalWeakTable<Type, LinkedList<WeakReference<Type>>> knownImplClasses =
+            new ConditionalWeakTable<Type, LinkedList<WeakReference<Type>>>();
 
-        internal static LinkedList<WeakReference> GetAttributeInterfaces(Type clazz)
+        internal static LinkedList<WeakReference<Type>> GetAttributeInterfaces(Type clazz)
         {
-            LinkedList<WeakReference> foundInterfaces = knownImplClasses.Get(clazz);
-            lock (knownImplClasses)
+            return knownImplClasses.GetValue(clazz, (key) =>
             {
-                if (foundInterfaces == null)
+                LinkedList<WeakReference<Type>> foundInterfaces = new LinkedList<WeakReference<Type>>();
+                // find all interfaces that this attribute instance implements
+                // and that extend the Attribute interface
+                Type actClazz = clazz;
+                do
                 {
-                    // we have the slight chance that another thread may do the same, but who cares?
-                    foundInterfaces = new LinkedList<WeakReference>();
-                    // find all interfaces that this attribute instance implements
-                    // and that extend the Attribute interface
-                    Type actClazz = clazz;
-                    do
+                    foreach (Type curInterface in actClazz.GetInterfaces())
                     {
-                        foreach (Type curInterface in actClazz.GetInterfaces())
+                        if (curInterface != typeof(IAttribute) && typeof(IAttribute).IsAssignableFrom(curInterface))
                         {
-                            if (curInterface != typeof(IAttribute) && (typeof(IAttribute)).IsAssignableFrom(curInterface))
-                            {
-                                foundInterfaces.AddLast(new WeakReference(curInterface));
-                            }
+                            foundInterfaces.AddLast(new WeakReference<Type>(curInterface));
                         }
-                        actClazz = actClazz.GetTypeInfo().BaseType;
-                    } while (actClazz != null);
-                    knownImplClasses.Put(clazz, foundInterfaces);
-                }
-            }
-            return foundInterfaces;
+                    }
+                    actClazz = actClazz.GetTypeInfo().BaseType;
+                } while (actClazz != null);
+
+                return foundInterfaces;
+            });
         }
 
         /// <summary>
@@ -307,12 +334,12 @@ namespace Lucene.Net.Util
                 return;
             }
 
-            LinkedList<WeakReference> foundInterfaces = GetAttributeInterfaces(clazz);
+            LinkedList<WeakReference<Type>> foundInterfaces = GetAttributeInterfaces(clazz);
 
             // add all interfaces of this Attribute to the maps
-            foreach (WeakReference curInterfaceRef in foundInterfaces)
+            foreach (var curInterfaceRef in foundInterfaces)
             {
-                Type curInterface = (Type)curInterfaceRef.Target;
+                curInterfaceRef.TryGetTarget(out Type curInterface);
                 Debug.Assert(curInterface != null, "We have a strong reference on the class holding the interfaces, so they should never get evicted");
                 // Attribute is a superclass of this interface
                 if (!attributes.ContainsKey(curInterface))
diff --git a/src/Lucene.Net/Util/VirtualMethod.cs b/src/Lucene.Net/Util/VirtualMethod.cs
index 7113a78..dfc0f8d 100644
--- a/src/Lucene.Net/Util/VirtualMethod.cs
+++ b/src/Lucene.Net/Util/VirtualMethod.cs
@@ -6,6 +6,7 @@ using System.Globalization;
 using System.Linq;
 #endif
 using System.Reflection;
+using System.Runtime.CompilerServices;
 
 namespace Lucene.Net.Util
 {
@@ -69,7 +70,52 @@ namespace Lucene.Net.Util
         private readonly Type baseClass;
         private readonly string method;
         private readonly Type[] parameters;
-        private readonly WeakIdentityMap<Type, int> cache = WeakIdentityMap<Type, int>.NewConcurrentHashMap(false);
+        // LUCENENET: Replaced IdentityHashMap with ConditionalWeakTable. A Type IS an identity, so there is
+        // no need for the extra IdentityWeakReference.
+        private readonly ConditionalWeakTable<Type, Int32Ref> cache = new ConditionalWeakTable<Type, Int32Ref>();
+
+        // LUCENENET specific wrapper needed because ConditionalWeakTable requires a reference type.
+        private class Int32Ref : IEquatable<Int32Ref>
+        {
+            private int value;
+
+            public Int32Ref(int value)
+            {
+                this.value = value;
+            }
+
+            public bool Equals(Int32Ref other)
+            {
+                if (other == null)
+                    return false;
+                return value.Equals(other);
+            }
+
+            public override bool Equals(object obj)
+            {
+                if (obj is Int32Ref other)
+                    return Equals(other);
+                if (obj is int otherInt)
+                    return value.Equals(otherInt);
+                return false;
+            }
+
+            public override int GetHashCode()
+            {
+                return value.GetHashCode();
+            }
+
+            public static implicit operator int(Int32Ref value)
+            {
+                return value.value;
+            }
+
+            public static implicit operator Int32Ref(int value)
+            {
+                return new Int32Ref(value);
+            }
+        }
+
 
         /// <summary>
         /// Creates a new instance for the given <paramref name="baseClass"/> and method declaration. </summary>
@@ -105,13 +151,8 @@ namespace Lucene.Net.Util
         /// <returns> 0 if and only if not overridden, else the distance to the base class. </returns>
         public int GetImplementationDistance(Type subclazz)
         {
-            int distance = cache.Get(subclazz);
-            if (distance == default(int))
-            {
-                // we have the slight chance that another thread may do the same, but who cares?
-                cache.Put(subclazz, distance = Convert.ToInt32(ReflectImplementationDistance(subclazz), CultureInfo.InvariantCulture));
-            }
-            return (int)distance;
+            // LUCENENET: Replaced WeakIdentityMap with ConditionalWeakTable - This operation is simplified over Lucene.
+            return cache.GetValue(subclazz, (key) => Convert.ToInt32(ReflectImplementationDistance(key), CultureInfo.InvariantCulture));
         }
 
         /// <summary>
diff --git a/src/Lucene.Net/Util/WeakIdentityMap.cs b/src/Lucene.Net/Util/WeakIdentityMap.cs
index 8444eb6..f2f55f4 100644
--- a/src/Lucene.Net/Util/WeakIdentityMap.cs
+++ b/src/Lucene.Net/Util/WeakIdentityMap.cs
@@ -1,398 +1,403 @@
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.Collections;
-
-namespace Lucene.Net.Util
-{
-    /*
-	 * Licensed to the Apache Software Foundation (ASF) under one or more
-	 * contributor license agreements.  See the NOTICE file distributed with
-	 * this work for additional information regarding copyright ownership.
-	 * The ASF licenses this file to You 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.
-	 */
-
-    /// <summary>
-    /// Implements a combination of <c>java.util.WeakHashMap</c> and
-    /// <c>java.util.IdentityHashMap</c>.
-    /// Useful for caches that need to key off of a <c>==</c> comparison
-    /// instead of a <c>.Equals(object)</c>.
-    ///
-    /// <para/>This class is not a general-purpose <see cref="IDictionary{TKey, TValue}"/>
-    /// implementation! It intentionally violates
-    /// <see cref="IDictionary{TKey, TValue}"/>'s general contract, which mandates the use of the <see cref="object.Equals(object)"/> method
-    /// when comparing objects. This class is designed for use only in the
-    /// rare cases wherein reference-equality semantics are required.
-    ///
-    /// <para/>This implementation was forked from <a href="http://cxf.apache.org/">Apache CXF</a>
-    /// but modified to <b>not</b> implement the <see cref="IDictionary{TKey, TValue}"/> interface and
-    /// without any set views on it, as those are error-prone and inefficient,
-    /// if not implemented carefully. The map only contains <see cref="IEnumerable{T}.GetEnumerator()"/> implementations
-    /// on the values and not-GCed keys. Lucene's implementation also supports <c>null</c>
-    /// keys, but those are never weak!
-    ///
-    /// <para/><a name="reapInfo" />The map supports two modes of operation:
-    /// <list type="bullet">
-    ///     <item><term><c>reapOnRead = true</c>:</term><description> This behaves identical to a <c>java.util.WeakHashMap</c>
-    ///         where it also cleans up the reference queue on every read operation (<see cref="Get(object)"/>,
-    ///         <see cref="ContainsKey(object)"/>, <see cref="Count"/>, <see cref="GetValueEnumerator()"/>), freeing map entries
-    ///         of already GCed keys.</description></item>
-    ///     <item><term><c>reapOnRead = false</c>:</term><description> This mode does not call <see cref="Reap()"/> on every read
-    ///         operation. In this case, the reference queue is only cleaned up on write operations
-    ///         (like <see cref="Put(TKey, TValue)"/>). This is ideal for maps with few entries where
-    ///         the keys are unlikely be garbage collected, but there are lots of <see cref="Get(object)"/>
-    ///         operations. The code can still call <see cref="Reap()"/> to manually clean up the queue without
-    ///         doing a write operation.</description></item>
-    /// </list>
-    /// <para/>
-    /// @lucene.internal
-    /// </summary>
-    public sealed class WeakIdentityMap<TKey, TValue>
-         where TKey : class
-    {
-        private readonly IDictionary<IdentityWeakReference, TValue> backingStore;
-
-        private readonly bool reapOnRead;
-
-        /// <summary>
-        /// Creates a new <see cref="WeakIdentityMap{TKey, TValue}"/> based on a non-synchronized <see cref="Dictionary{TKey, TValue}"/>.
-        /// The map <a href="#reapInfo">cleans up the reference queue on every read operation</a>.
-        /// </summary>
-        public static WeakIdentityMap<TKey, TValue> NewHashMap()
-        {
-            return NewHashMap(false);
-        }
-
-        /// <summary>
-        /// Creates a new <see cref="WeakIdentityMap{TKey, TValue}"/> based on a non-synchronized <see cref="Dictionary{TKey, TValue}"/>. </summary>
-        /// <param name="reapOnRead"> controls if the map <a href="#reapInfo">cleans up the reference queue on every read operation</a>. </param>
-        public static WeakIdentityMap<TKey, TValue> NewHashMap(bool reapOnRead)
-        {
-            return new WeakIdentityMap<TKey, TValue>(new Dictionary<IdentityWeakReference, TValue>(), reapOnRead);
-        }
-
-        /// <summary>
-        /// Creates a new <see cref="WeakIdentityMap{TKey, TValue}"/> based on a <see cref="ConcurrentDictionary{TKey, TValue}"/>.
-        /// The map <a href="#reapInfo">cleans up the reference queue on every read operation</a>.
-        /// </summary>
-        public static WeakIdentityMap<TKey, TValue> NewConcurrentHashMap()
-        {
-            return NewConcurrentHashMap(true);
-        }
-
-        /// <summary>
-        /// Creates a new <see cref="WeakIdentityMap{TKey, TValue}"/> based on a <see cref="ConcurrentDictionary{TKey, TValue}"/>. </summary>
-        /// <param name="reapOnRead"> controls if the map <a href="#reapInfo">cleans up the reference queue on every read operation</a>. </param>
-        public static WeakIdentityMap<TKey, TValue> NewConcurrentHashMap(bool reapOnRead)
-        {
-            return new WeakIdentityMap<TKey, TValue>(new ConcurrentDictionary<IdentityWeakReference, TValue>(), reapOnRead);
-        }
-
-        /// <summary>
-        /// Private only constructor, to create use the static factory methods. </summary>
-        private WeakIdentityMap(IDictionary<IdentityWeakReference, TValue> backingStore, bool reapOnRead)
-        {
-            this.backingStore = backingStore;
-            this.reapOnRead = reapOnRead;
-        }
-
-        /// <summary>
-        /// Removes all of the mappings from this map. </summary>
-        public void Clear()
-        {
-            backingStore.Clear();
-            Reap();
-        }
-
-        /// <summary>
-        /// Returns <c>true</c> if this map contains a mapping for the specified key. </summary>
-        public bool ContainsKey(object key)
-        {
-            if (reapOnRead)
-            {
-                Reap();
-            }
-            return backingStore.ContainsKey(new IdentityWeakReference(key));
-        }
-
-        /// <summary>
-        /// Returns the value to which the specified key is mapped. </summary>
-        public TValue Get(object key)
-        {
-            if (reapOnRead)
-            {
-                Reap();
-            }
-
-            TValue val;
-            if (backingStore.TryGetValue(new IdentityWeakReference(key), out val))
-            {
-                return val;
-            }
-            else
-            {
-                return default(TValue);
-            }
-        }
-
-        /// <summary>
-        /// Associates the specified value with the specified key in this map.
-        /// If the map previously contained a mapping for this key, the old value
-        /// is replaced.
-        /// </summary>
-        public TValue Put(TKey key, TValue value)
-        {
-            Reap();
-            return backingStore[new IdentityWeakReference(key)] = value;
-        }
-
-        /// <summary>
-        /// Gets an <see cref="IEnumerable{TKey}"/> object containing the keys of the <see cref="WeakIdentityMap{TKey, TValue}"/>.
-        /// </summary>
-        public IEnumerable<TKey> Keys
-        {
-            get
-            {
-                return new KeyWrapper(this);
-            }
-        }
-
-        /// <summary>
-        /// LUCENENET specific class to allow the 
-        /// GetEnumerator() method to be overridden
-        /// for the keys so we can return an enumerator
-        /// that is smart enough to clean up the dead keys
-        /// and also so that MoveNext() returns false in the
-        /// event there are no more values left (instead of returning
-        /// a null value in an extra enumeration).
-        /// </summary>
-        private class KeyWrapper : IEnumerable<TKey>
-        {
-            private readonly WeakIdentityMap<TKey, TValue> outerInstance;
-            public KeyWrapper(WeakIdentityMap<TKey, TValue> outerInstance)
-            {
-                this.outerInstance = outerInstance;
-            }
-            public IEnumerator<TKey> GetEnumerator()
-            {
-                outerInstance.Reap();
-                return new IteratorAnonymousInnerClassHelper(outerInstance);
-            }
-
-            IEnumerator IEnumerable.GetEnumerator()
-            {
-                return GetEnumerator();
-            }
-        }
-
-        /// <summary>
-        /// Gets an <see cref="IEnumerable{TKey}"/> object containing the values of the <see cref="WeakIdentityMap{TKey, TValue}"/>.
-        /// </summary>
-        public IEnumerable<TValue> Values
-        {
-            get
-            {
-                if (reapOnRead) Reap();
-                return backingStore.Values;
-            }
-        }
-
-        /// <summary>
-        /// Returns <c>true</c> if this map contains no key-value mappings. </summary>
-        public bool IsEmpty
-        {
-            get
-            {
-                return Count == 0;
-            }
-        }
-
-        /// <summary>
-        /// Removes the mapping for a key from this weak hash map if it is present.
-        /// Returns the value to which this map previously associated the key,
-        /// or <c>null</c> if the map contained no mapping for the key.
-        /// A return value of <c>null</c> does not necessarily indicate that
-        /// the map contained.
-        /// </summary>
-        public bool Remove(object key)
-        {
-            Reap();
-            return backingStore.Remove(new IdentityWeakReference(key));
-        }
-
-        /// <summary>
-        /// Returns the number of key-value mappings in this map. This result is a snapshot,
-        /// and may not reflect unprocessed entries that will be removed before next
-        /// attempted access because they are no longer referenced.
-        /// <para/>
-        /// NOTE: This was size() in Lucene.
-        /// </summary>
-        public int Count
-        {
-            get
-            {
-                if (backingStore.Count == 0)
-                {
-                    return 0;
-                }
-                if (reapOnRead)
-                {
-                    Reap();
-                }
-                return backingStore.Count;
-            }
-        }
-
-        private class IteratorAnonymousInnerClassHelper : IEnumerator<TKey>
-        {
-            private readonly WeakIdentityMap<TKey, TValue> outerInstance;
-            private readonly IEnumerator<KeyValuePair<IdentityWeakReference, TValue>> enumerator;
-
-            public IteratorAnonymousInnerClassHelper(WeakIdentityMap<TKey, TValue> outerInstance)
-            {
-                this.outerInstance = outerInstance;
-                enumerator = outerInstance.backingStore.GetEnumerator();
-            }
-
-            // holds strong reference to next element in backing iterator:
-            private object next = null;
-
-            public TKey Current
-            {
-                get
-                {
-                    return (TKey)next;
-                }
-            }
-
-            object IEnumerator.Current
-            {
-                get
-                {
-                    return Current;
-                }
-            }
-
-
-            public void Dispose()
-            {
-                enumerator.Dispose();
-            }
-
-
-            public bool MoveNext()
-            {
-                while (enumerator.MoveNext())
-                {
-                    next = enumerator.Current.Key.Target;
-                    if (next != null)
-                    {
-                        // unfold "null" special value:
-                        if (next == NULL)
-                            next = null;
-                        return true;
-                    }
-                }
-                return false;
-            }
-
-            public void Reset()
-            {
-                enumerator.Reset();
-            }
-        }
-
-        /// <summary>
-        /// Returns an iterator over all values of this map.
-        /// This iterator may return values whose key is already
-        /// garbage collected while iterator is consumed,
-        /// especially if <see cref="reapOnRead"/> is <c>false</c>.
-        /// <para/>
-        /// NOTE: This was valueIterator() in Lucene.
-        /// </summary>
-        public IEnumerator<TValue> GetValueEnumerator()
-        {
-            if (reapOnRead)
-            {
-                Reap();
-            }
-            return backingStore.Values.GetEnumerator();
-        }
-
-        /// <summary>
-        /// 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 <c>reapOnRead = true</c>. Otherwise it might be a good idea
-        /// to call this method when there is spare time (e.g. from a background thread). 
-        /// <a href="#reapInfo">Information about the <c>reapOnRead</c> setting</a>		
-        /// </summary>
-        public void Reap()
-        {
-            List<IdentityWeakReference> keysToRemove = null;
-            foreach (var item in backingStore)
-            {
-                if (!item.Key.IsAlive)
-                {
-                    // create the list of keys to remove only if there are keys to remove.
-                    // this reduces heap pressure
-                    if (keysToRemove == null)
-                        keysToRemove = new List<IdentityWeakReference>();
-                    keysToRemove.Add(item.Key);
-                }
-            }
-
-            if (keysToRemove != null)
-                foreach (var key in keysToRemove)
-                {
-                    backingStore.Remove(key);
-                }
-        }
-
-        // we keep a hard reference to our NULL key, so map supports null keys that never get GCed:
-        internal static readonly object NULL = new object();
-
-        private sealed class IdentityWeakReference : WeakReference
-        {
-            private readonly int hash;
-
-            internal IdentityWeakReference(object obj/*, ReferenceQueue<object> queue*/)
-                : base(obj == null ? NULL : obj/*, queue*/)
-            {
-                hash = RuntimeHelpers.GetHashCode(obj);
-            }
-
-            public override int GetHashCode()
-            {
-                return hash;
-            }
-
-            public override bool Equals(object o)
-            {
-                if (this == o)
-                {
-                    return true;
-                }
-                IdentityWeakReference @ref = o as IdentityWeakReference;
-                if (@ref != null)
-                {
-                    if (this.Target == @ref.Target)
-                    {
-                        return true;
-                    }
-                }
-                return false;
-            }
-        }
-    }
-}
\ No newline at end of file
+// LUCENENET specific - factored out this class and replaced it with ConditionalWeakTable<TKey, TValue>.
+// ConditionalWeakTable<TKey, TValue> is thread-safe and internally uses RuntimeHelpers.GetHashCode()
+// to lookup the key, so it can be used as a direct replacement for WeakIdentityMap<TKey, TValue>
+// in most cases.
+
+//using System;
+//using System.Collections.Concurrent;
+//using System.Collections.Generic;
+//using System.Runtime.CompilerServices;
+//using System.Collections;
+
+//namespace Lucene.Net.Util
+//{
+//    /*
+//	 * Licensed to the Apache Software Foundation (ASF) under one or more
+//	 * contributor license agreements.  See the NOTICE file distributed with
+//	 * this work for additional information regarding copyright ownership.
+//	 * The ASF licenses this file to You 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.
+//	 */
+
+//    /// <summary>
+//    /// Implements a combination of <c>java.util.WeakHashMap</c> and
+//    /// <c>java.util.IdentityHashMap</c>.
+//    /// Useful for caches that need to key off of a <c>==</c> comparison
+//    /// instead of a <c>.Equals(object)</c>.
+//    ///
+//    /// <para/>This class is not a general-purpose <see cref="IDictionary{TKey, TValue}"/>
+//    /// implementation! It intentionally violates
+//    /// <see cref="IDictionary{TKey, TValue}"/>'s general contract, which mandates the use of the <see cref="object.Equals(object)"/> method
+//    /// when comparing objects. This class is designed for use only in the
+//    /// rare cases wherein reference-equality semantics are required.
+//    ///
+//    /// <para/>This implementation was forked from <a href="http://cxf.apache.org/">Apache CXF</a>
+//    /// but modified to <b>not</b> implement the <see cref="IDictionary{TKey, TValue}"/> interface and
+//    /// without any set views on it, as those are error-prone and inefficient,
+//    /// if not implemented carefully. The map only contains <see cref="IEnumerable{T}.GetEnumerator()"/> implementations
+//    /// on the values and not-GCed keys. Lucene's implementation also supports <c>null</c>
+//    /// keys, but those are never weak!
+//    ///
+//    /// <para/><a name="reapInfo" />The map supports two modes of operation:
+//    /// <list type="bullet">
+//    ///     <item><term><c>reapOnRead = true</c>:</term><description> This behaves identical to a <c>java.util.WeakHashMap</c>
+//    ///         where it also cleans up the reference queue on every read operation (<see cref="Get(object)"/>,
+//    ///         <see cref="ContainsKey(object)"/>, <see cref="Count"/>, <see cref="GetValueEnumerator()"/>), freeing map entries
+//    ///         of already GCed keys.</description></item>
+//    ///     <item><term><c>reapOnRead = false</c>:</term><description> This mode does not call <see cref="Reap()"/> on every read
+//    ///         operation. In this case, the reference queue is only cleaned up on write operations
+//    ///         (like <see cref="Put(TKey, TValue)"/>). This is ideal for maps with few entries where
+//    ///         the keys are unlikely be garbage collected, but there are lots of <see cref="Get(object)"/>
+//    ///         operations. The code can still call <see cref="Reap()"/> to manually clean up the queue without
+//    ///         doing a write operation.</description></item>
+//    /// </list>
+//    /// <para/>
+//    /// @lucene.internal
+//    /// </summary>
+//    public sealed class WeakIdentityMap<TKey, TValue>
+//         where TKey : class
+//    {
+//        private readonly IDictionary<IdentityWeakReference, TValue> backingStore;
+
+//        private readonly bool reapOnRead;
+
+//        /// <summary>
+//        /// Creates a new <see cref="WeakIdentityMap{TKey, TValue}"/> based on a non-synchronized <see cref="Dictionary{TKey, TValue}"/>.
+//        /// The map <a href="#reapInfo">cleans up the reference queue on every read operation</a>.
+//        /// </summary>
+//        public static WeakIdentityMap<TKey, TValue> NewHashMap()
+//        {
+//            return NewHashMap(false);
+//        }
+
+//        /// <summary>
+//        /// Creates a new <see cref="WeakIdentityMap{TKey, TValue}"/> based on a non-synchronized <see cref="Dictionary{TKey, TValue}"/>. </summary>
+//        /// <param name="reapOnRead"> controls if the map <a href="#reapInfo">cleans up the reference queue on every read operation</a>. </param>
+//        public static WeakIdentityMap<TKey, TValue> NewHashMap(bool reapOnRead)
+//        {
+//            return new WeakIdentityMap<TKey, TValue>(new Dictionary<IdentityWeakReference, TValue>(), reapOnRead);
+//        }
+
+//        /// <summary>
+//        /// Creates a new <see cref="WeakIdentityMap{TKey, TValue}"/> based on a <see cref="ConcurrentDictionary{TKey, TValue}"/>.
+//        /// The map <a href="#reapInfo">cleans up the reference queue on every read operation</a>.
+//        /// </summary>
+//        public static WeakIdentityMap<TKey, TValue> NewConcurrentHashMap()
+//        {
+//            return NewConcurrentHashMap(true);
+//        }
+
+//        /// <summary>
+//        /// Creates a new <see cref="WeakIdentityMap{TKey, TValue}"/> based on a <see cref="ConcurrentDictionary{TKey, TValue}"/>. </summary>
+//        /// <param name="reapOnRead"> controls if the map <a href="#reapInfo">cleans up the reference queue on every read operation</a>. </param>
+//        public static WeakIdentityMap<TKey, TValue> NewConcurrentHashMap(bool reapOnRead)
+//        {
+//            return new WeakIdentityMap<TKey, TValue>(new ConcurrentDictionary<IdentityWeakReference, TValue>(), reapOnRead);
+//        }
+
+//        /// <summary>
+//        /// Private only constructor, to create use the static factory methods. </summary>
+//        private WeakIdentityMap(IDictionary<IdentityWeakReference, TValue> backingStore, bool reapOnRead)
+//        {
+//            this.backingStore = backingStore;
+//            this.reapOnRead = reapOnRead;
+//        }
+
+//        /// <summary>
+//        /// Removes all of the mappings from this map. </summary>
+//        public void Clear()
+//        {
+//            backingStore.Clear();
+//            Reap();
+//        }
+
+//        /// <summary>
+//        /// Returns <c>true</c> if this map contains a mapping for the specified key. </summary>
+//        public bool ContainsKey(object key)
+//        {
+//            if (reapOnRead)
+//            {
+//                Reap();
+//            }
+//            return backingStore.ContainsKey(new IdentityWeakReference(key));
+//        }
+
+//        /// <summary>
+//        /// Returns the value to which the specified key is mapped. </summary>
+//        public TValue Get(object key)
+//        {
+//            if (reapOnRead)
+//            {
+//                Reap();
+//            }
+
+//            TValue val;
+//            if (backingStore.TryGetValue(new IdentityWeakReference(key), out val))
+//            {
+//                return val;
+//            }
+//            else
+//            {
+//                return default(TValue);
+//            }
+//        }
+
+//        /// <summary>
+//        /// Associates the specified value with the specified key in this map.
+//        /// If the map previously contained a mapping for this key, the old value
+//        /// is replaced.
+//        /// </summary>
+//        public TValue Put(TKey key, TValue value)
+//        {
+//            Reap();
+//            return backingStore[new IdentityWeakReference(key)] = value;
+//        }
+
+//        /// <summary>
+//        /// Gets an <see cref="IEnumerable{TKey}"/> object containing the keys of the <see cref="WeakIdentityMap{TKey, TValue}"/>.
+//        /// </summary>
+//        public IEnumerable<TKey> Keys
+//        {
+//            get
+//            {
+//                return new KeyWrapper(this);
+//            }
+//        }
+
+//        /// <summary>
+//        /// LUCENENET specific class to allow the 
+//        /// GetEnumerator() method to be overridden
+//        /// for the keys so we can return an enumerator
+//        /// that is smart enough to clean up the dead keys
+//        /// and also so that MoveNext() returns false in the
+//        /// event there are no more values left (instead of returning
+//        /// a null value in an extra enumeration).
+//        /// </summary>
+//        private class KeyWrapper : IEnumerable<TKey>
+//        {
+//            private readonly WeakIdentityMap<TKey, TValue> outerInstance;
+//            public KeyWrapper(WeakIdentityMap<TKey, TValue> outerInstance)
+//            {
+//                this.outerInstance = outerInstance;
+//            }
+//            public IEnumerator<TKey> GetEnumerator()
+//            {
+//                outerInstance.Reap();
+//                return new IteratorAnonymousInnerClassHelper(outerInstance);
+//            }
+
+//            IEnumerator IEnumerable.GetEnumerator()
+//            {
+//                return GetEnumerator();
+//            }
+//        }
+
+//        /// <summary>
+//        /// Gets an <see cref="IEnumerable{TKey}"/> object containing the values of the <see cref="WeakIdentityMap{TKey, TValue}"/>.
+//        /// </summary>
+//        public IEnumerable<TValue> Values
+//        {
+//            get
+//            {
+//                if (reapOnRead) Reap();
+//                return backingStore.Values;
+//            }
+//        }
+
+//        /// <summary>
+//        /// Returns <c>true</c> if this map contains no key-value mappings. </summary>
+//        public bool IsEmpty
+//        {
+//            get
+//            {
+//                return Count == 0;
+//            }
+//        }
+
+//        /// <summary>
+//        /// Removes the mapping for a key from this weak hash map if it is present.
+//        /// Returns the value to which this map previously associated the key,
+//        /// or <c>null</c> if the map contained no mapping for the key.
+//        /// A return value of <c>null</c> does not necessarily indicate that
+//        /// the map contained.
+//        /// </summary>
+//        public bool Remove(object key)
+//        {
+//            Reap();
+//            return backingStore.Remove(new IdentityWeakReference(key));
+//        }
+
+//        /// <summary>
+//        /// Returns the number of key-value mappings in this map. This result is a snapshot,
+//        /// and may not reflect unprocessed entries that will be removed before next
+//        /// attempted access because they are no longer referenced.
+//        /// <para/>
+//        /// NOTE: This was size() in Lucene.
+//        /// </summary>
+//        public int Count
+//        {
+//            get
+//            {
+//                if (backingStore.Count == 0)
+//                {
+//                    return 0;
+//                }
+//                if (reapOnRead)
+//                {
+//                    Reap();
+//                }
+//                return backingStore.Count;
+//            }
+//        }
+
+//        private class IteratorAnonymousInnerClassHelper : IEnumerator<TKey>
+//        {
+//            private readonly WeakIdentityMap<TKey, TValue> outerInstance;
+//            private readonly IEnumerator<KeyValuePair<IdentityWeakReference, TValue>> enumerator;
+
+//            public IteratorAnonymousInnerClassHelper(WeakIdentityMap<TKey, TValue> outerInstance)
+//            {
+//                this.outerInstance = outerInstance;
+//                enumerator = outerInstance.backingStore.GetEnumerator();
+//            }
+
+//            // holds strong reference to next element in backing iterator:
+//            private object next = null;
+
+//            public TKey Current
+//            {
+//                get
+//                {
+//                    return (TKey)next;
+//                }
+//            }
+
+//            object IEnumerator.Current
+//            {
+//                get
+//                {
+//                    return Current;
+//                }
+//            }
+
+
+//            public void Dispose()
+//            {
+//                enumerator.Dispose();
+//            }
+
+
+//            public bool MoveNext()
+//            {
+//                while (enumerator.MoveNext())
+//                {
+//                    next = enumerator.Current.Key.Target;
+//                    if (next != null)
+//                    {
+//                        // unfold "null" special value:
+//                        if (next == NULL)
+//                            next = null;
+//                        return true;
+//                    }
+//                }
+//                return false;
+//            }
+
+//            public void Reset()
+//            {
+//                enumerator.Reset();
+//            }
+//        }
+
+//        /// <summary>
+//        /// Returns an iterator over all values of this map.
+//        /// This iterator may return values whose key is already
+//        /// garbage collected while iterator is consumed,
+//        /// especially if <see cref="reapOnRead"/> is <c>false</c>.
+//        /// <para/>
+//        /// NOTE: This was valueIterator() in Lucene.
+//        /// </summary>
+//        public IEnumerator<TValue> GetValueEnumerator()
+//        {
+//            if (reapOnRead)
+//            {
+//                Reap();
+//            }
+//            return backingStore.Values.GetEnumerator();
+//        }
+
+//        /// <summary>
+//        /// 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 <c>reapOnRead = true</c>. Otherwise it might be a good idea
+//        /// to call this method when there is spare time (e.g. from a background thread). 
+//        /// <a href="#reapInfo">Information about the <c>reapOnRead</c> setting</a>		
+//        /// </summary>
+//        public void Reap()
+//        {
+//            List<IdentityWeakReference> keysToRemove = null;
+//            foreach (var item in backingStore)
+//            {
+//                if (!item.Key.IsAlive)
+//                {
+//                    // create the list of keys to remove only if there are keys to remove.
+//                    // this reduces heap pressure
+//                    if (keysToRemove == null)
+//                        keysToRemove = new List<IdentityWeakReference>();
+//                    keysToRemove.Add(item.Key);
+//                }
+//            }
+
+//            if (keysToRemove != null)
+//                foreach (var key in keysToRemove)
+//                {
+//                    backingStore.Remove(key);
+//                }
+//        }
+
+//        // we keep a hard reference to our NULL key, so map supports null keys that never get GCed:
+//        internal static readonly object NULL = new object();
+
+//        private sealed class IdentityWeakReference : WeakReference
+//        {
+//            private readonly int hash;
+
+//            internal IdentityWeakReference(object obj/*, ReferenceQueue<object> queue*/)
+//                : base(obj == null ? NULL : obj/*, queue*/)
+//            {
+//                hash = RuntimeHelpers.GetHashCode(obj);
+//            }
+
+//            public override int GetHashCode()
+//            {
+//                return hash;
+//            }
+
+//            public override bool Equals(object o)
+//            {
+//                if (this == o)
+//                {
+//                    return true;
+//                }
+//                IdentityWeakReference @ref = o as IdentityWeakReference;
+//                if (@ref != null)
+//                {
+//                    if (this.Target == @ref.Target)
+//                    {
+//                        return true;
+//                    }
+//                }
+//                return false;
+//            }
+//        }
+//    }
+//}
\ No newline at end of file