我惊讶地发现我的应用程序内存占用不断增长 - 运行的时间越长,消耗的内存就越多。因此,windbg
的一些魔力让我根据ConcurrentDictionary
向我的小LRU缓存指出了一个问题。 CD有很多好处,对我来说非常酷(其中一个是它的数据永远不会在LOH中结束)。 TryAdd
和TryRemove
是用于添加和逐出项目的两种方法。一些旧元素的!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;
}