I have a cache consisting of elements each of which contains two concurrent dictionary and I think they may be responsible for memory leak in my application. I'm frequently adding and removing stuff from this dictionary. Can someone provide me description of how memory allocation works for concurrent dictionary and what the best practice would be in my case? Thanks in advance!
-
1None of the classes in .NET explicitly release allocated memory unless they are some specialty class designed to do just that. It will release the references, but it is up to to the garbage collector to de-allocate the memory for the object. I would recommend running a profiler on your program that can track object lifetimes and memory consumption and find out where your leek really is instead of guessing where it is.Scott Chamberlain– Scott Chamberlain2013-12-11 07:56:54 +00:00Commented Dec 11, 2013 at 7:56
-
Also if you are using 4.5.1 there are some nice new classes to 4.0 for building caches of objects.Scott Chamberlain– Scott Chamberlain2013-12-11 07:59:26 +00:00Commented Dec 11, 2013 at 7:59
-
Thanks, that's what I really meant "will GC release memory which was used by removed element". So If I have a concurrent dictionary with some amount of elements, then I remove element from it and then after some time pass total amount of memory used by appication will decreased? Application use about 45GB of memory so it's kind of hard to use even WinDbg in this case. Other profiles stops working after application starts to use ~4GB.Lionia Vasilev– Lionia Vasilev2013-12-11 08:08:44 +00:00Commented Dec 11, 2013 at 8:08
-
You need to tell more about the "elements" and what you're doing with them. Maybe there are some remaining references?JeffRSon– JeffRSon2013-12-11 08:11:07 +00:00Commented Dec 11, 2013 at 8:11
3 Answers
Any .NET collection's remove method will not free the memory, it will simply remove the reference from the collection. If nothing else references those objects, the garbage collector will eventually clean them up.
A common cause of references being held are forgetting to unwire event handlers.
Several .NET Memory profilers such as MemProfiler, dotTrace and ANTS memory profiler have trial versions.
As @Scott Chamberlain points out, the System.Runtime.Caching Namespace contains types that let you implement caching in NET Framework applications.
1 Comment
ConcurrentBag implementation actually didn't always remove references immediately upon consuming them: stackoverflow.com/questions/5353164/…If the elements you are removing make use of unmanaged memory (bitmaps etc come to mind) these elements should implement IDisposable and you should call .Dispose before the last reference goes out of scope.
1 Comment
No, the ConcurrentDictionary<K,V>.TryRemove method doesn't release all allocated memory. It releases the memory associated with the removed key (a Node), but it doesn't shrink the array where the nodes are stored. The amount of memory wasted by a partially filled ConcurrentDictionary<K,V> is significantly less than the amount of memory wasted by a partially filled Dictionary<K,V>, and that's why currently (.NET 9) the ConcurrentDictionary<K,V> is not equipped with an API similar to the Dictionary<K,V>.TrimExcess.
Adding the TrimExcess to the ConcurrentDictionary<K,V> has been proposed in this GitHub proposal, but it didn't go anywhere and it was eventually closed. Quoting from this proposal:
ConcurrentDictionarystores its data differently than does aDictionary; the vast majority of memory it consumes is stored in nodes per element, rather than one large array.
ConcurrentDictionary._tables._bucketsis aNodearray, whose length can be even greater than the number of elements, and this array will not shrink when the elements are removed.Yes, but all it stores are the node references; with
Dictionary, each entry stores both theTKeyand theTValueand additional state (a hash code and next link).The references also occupy a lot of memory. On 64-bit systems, for simple key/value, e.g.
ConcurrentDictionary<int, int>, a reference takes about 40% memory of a Node.A reference is 8 bytes on 64-bit. Can you please share code for the scenario that's causing you to want such a method?