i have a class that has a ConcurrentDictionary as a private member.
This class also defines a delegate/call back method.
The base class registers this method as the callback for an external event. this is one only ONCE.
I am running ANT memory profiler and i’m seeing 1000s of instances of MyObj referenced from hundreds of instances of the ConcurrentDictionary property. The GC root for these is the event callback.
This seems to be causing memory to rise significantly as the application runs..after maybe about 5 min or so, a good portion of that memory is reclaimed, but i’m worried that the app could potentially run into issue since it swells sto quickly and for so long before GC kicks in.
What’s going on here and how do i resolve?
This is a snippet of the base calls that registers the handler
protected abstract void DataReceivedEventHandler(DataChangedEvent evt);
public virtual void RegisterForChanges(ICollection<MemoryTable> tables)
{
foreach (MemoryTable table in tables)
{
_subscribedTables.Add(table);
table.RegisterEventListener(new DataChangedCallBack(this.DataReceivedEventHandler));
}
}
Here is the handler which is implemented in a subclass of the above mentioned baseclass:
private ConcurrentDictionary<string, DataRecord> _cachedRecords;
protected override void DataReceivedEventHandler(DataChangedEvent evt)
{
DataRecord record = evt.Record as DataRecord;
string key = record.Key;
if (string.IsNullOrEmpty(key)) { return; }
if (_cachedRecords.ContainsKey(key))
{
_cachedRecords[key] = record;
DateTime updateTime = record.UpdateTime;
TimeSpan delta = updateTime - _lastNotifyTime;
if (delta.TotalMilliseconds > _notificationFrequency)
{
PublishData(updateTime);
}
}
}
The publishData method publishes prism events
Is it possible that you are re-subscribing tables over and over again? I see this:
and I’d expect to see a check to make sure that tables are not being re-subscribed:
EDIT: Given the comments at the beginning of the question, I am fairly confident that the problem (if you can call it a problem) lies here:
What you are saying here is that if the record’s key already exists in
cachedRecords, then replace the value with the (presumably) new row instance. This is probably because some background process caused the row’s data to be changed and you need to propagate those new values to the UI.My guess is that the MemoryTable class is creating a new instance of DataRecord for these changes, and sending that new instance up the event chain to the handler we see here. If the event is fired thousands of times, then of course you are going to end up with thousands of them in memory. The garbage collector is usually pretty good about cleaning these things up, but you might want to consider in-place updates to avoid the massive GC that will occur when these instances get collected.
What you should not do is try to control (or even predict) when the GC is going to run. Just make sure that after the GC collects, the excess objects are gone (in other words, make sure that they are not being leaked) and you will be alright.