问题
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