In part 3, we'll explore how to use the LeanSentry Memory diagnostic report to actually optimize memory usage in a .NET application.
You can also read part 1: Find application memory leaks with LeanSentry
and part 2: Determine the cause of a memory leak using LeanSentry's memory diagnostics to learn how to use LeanSentry Memory diagnostics.
Cause of the memory leak
Continuing with the report we examined in part 2: Determine the cause of a memory leak using LeanSentry's memory diagnostics:
- Most of the objects are in Gen2 and are String and ParsedArticle objects.
- We checked the reference paths and determined that there is a Dictionary<String, Content> that references Content objects that in turn reference String objects that remain on the heap.
Locating the issue in the source code
We will try to find and possibly break the top-most link in the reference chain. The objects that are down in the reference path are eligible for garbage collection.
(a) 3 objects eligible for GC:
(b) 1 object eligible for GC.
From the report, we know that one of the reference paths is as follows:
Dictionary<string, Content> → Content → String → Gen 2
In the application code, we notice that there is a Dictionary<string, Content> called ContentCache. It's solve purpose is to avoid expensive SQL calls by storing Content objects.
The key format that is chosen is id-date. For example, one of the keys could be 23-3/5/2020. The idea behind this is to fetch the content from the SQL server not more than once a day.
Next, we identify where in the application are we filling this dictionary with the Content objects. We see that this takes place in the FetchContent function.
Now we look for the expected place where we would be removing the Content objects from the ContentCache. In our code, we can see that the RemoveStaleFromPreviousDay function is responsible for this.
Using LeanSentry's hang diagnostics we have already determined that the application is suffering from a memory leak. The objects in the Cache are not being removed and are staying in the memory (see reference path determined).
Having a closer look at the FetchContent function, we notice a bug!
The 'now' object represents the date including the time. This is used in the cache key. Since time is continuously changing, every time the FetchArticle function is called another copy of a Content object is added to the ContentCache! Moreover, it is never released since the RemoveStaleFromPreviousDay function also used DateTime.UtcNow to find the keys.
Making the fix
Making the fix is fairly simple here. We have identified the bug. We need to use just the date instead of the date with time.
You can use LeanSentry Memory diagnostics to identify and diagnose application memory leaks.
For more information on how to get started with LeanSentry Memory diagnostics, see part 1: Find application memory leaks with LeanSentry and part 2: Determine the cause of a memory leak using LeanSentry's memory diagnostics.
Memory leaks can often be complex and an advanced analysis might be required to determine the optimal strategy for addressing them. If you have a complex memory leak, please contact LeanSentry support for additional assistance options.