ConcurrentDictionary 在删除元素时似乎没有为 GC 标记元素

Sch*_*999 5 c# collections concurrency garbage-collection .net-4.0

我惊讶地发现我的应用程序内存占用不断增长 - 运行时间越长,消耗的内存就越多。因此,windbg我用一些魔法指出了我基于ConcurrentDictionary. CD 有很多对我来说非常酷的好处(其中之一是它的数据永远不会在 LOH 中结束)。TryAddTryRemove是用于添加和驱逐项目的两种方法。!gcroot一些较旧的元素引导我回到我的缓存。ILSpy 的一些调查使我得出了这个结论:

TryRemove没有真正删除一个元素。它所做的只是更改链表指针以跳过从不将数组元素的值分配给null. 这可以防止 GC 收集旧的驱逐对象。

真的吗?这是一个已知的问题吗?如果是这样,我唯一的选择是TryUpdate(key, null)然后TryRemove(key)?如果是这样,那么我必须锁定ConcurrentDictionary访问权限,这是矛盾的。

这是 ILSpy 转储:

// 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;
}
Run Code Online (Sandbox Code Playgroud)