ConcurrentDictionary doesn't seem to mark elements for GC when they are removed

混江龙づ霸主 提交于 2019-12-23 15:10:31

问题


I was surprised to find that my app memory footprint kept growing - the longer it run, the more memory it consumed. So with some magic of windbg I pinpointed a problem to my little LRU cache based on ConcurrentDictionary. The CD has bunch of benefits that were very cool for me (one of which is that its data never ends up in LOH). TryAdd and TryRemove are two methods used to add and evict items. !gcroot of some older element lead me back to my cache. Some investigation with ILSpy led me to this conclusion:

TryRemove does not really remove an element. All it does is changing linked list pointers to skip never assigning the value of an array element to null. This prevent GC from collecting old evicted objects.

Really? Is that a known problem? If so is my only option is TryUpdate(key, null) and then TryRemove(key)? If so then I have to have locking around ConcurrentDictionary access which is oxymoronic.

Here is ILSpy dump:

// System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>
private bool TryRemoveInternal(TKey key, out TValue value, bool matchValue, TValue oldValue)
{
    while (true)
    {
        ConcurrentDictionary<TKey, TValue>.Tables tables = this.m_tables;
        int num;
        int num2;
        this.GetBucketAndLockNo(this.m_comparer.GetHashCode(key), out num, out num2, tables.m_buckets.Length, tables.m_locks.Length);
        lock (tables.m_locks[num2])
        {
            if (tables != this.m_tables)
            {
                continue;
            }
            ConcurrentDictionary<TKey, TValue>.Node node = null;
            ConcurrentDictionary<TKey, TValue>.Node node2 = tables.m_buckets[num];
            while (node2 != null)
            {
                if (this.m_comparer.Equals(node2.m_key, key))
                {
                    bool result;
                    if (matchValue && !EqualityComparer<TValue>.Default.Equals(oldValue, node2.m_value))
                    {
                        value = default(TValue);
                        result = false;
                        return result;
                    }
                    if (node == null)
                    {
                        Volatile.Write<ConcurrentDictionary<TKey, TValue>.Node>(ref tables.m_buckets[num], node2.m_next);
                    }
                    else
                    {
                        node.m_next = node2.m_next;
                    }
                    value = node2.m_value;
                    tables.m_countPerLock[num2]--;
                    result = true;
                    return result;
                }
                else
                {
                    node = node2;
                    node2 = node2.m_next;
                }
            }
        }
        break;
    }
    value = default(TValue);
    return false;
}

来源:https://stackoverflow.com/questions/21866642/concurrentdictionary-doesnt-seem-to-mark-elements-for-gc-when-they-are-removed

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!