Reputation: 2015
I am trying to figure out the cause of a (possible) memory leak in a complex C# based application. As I cannot debug the problem at runtime due to the system becoming very slow and unstable because of the high CPU and memory usage of the application, I created a dump (dmp) file of the application via the task manager at runtime and then opened that dump for further analysis in my local Visual Studio 2022 instance. When I now try to view the threads of the application at the point of time when the dump was created, it only shows me the call stack until where something async is executed. Everything that happens inside this async execution seems impossible to access. In the call stack it just says that it is waiting for an async process and I should double click or press enter to show async call stacks. However, this doesn't seem to do anything. Am I missing something, or how can I see what is happening inside the async process?
Upvotes: 1
Views: 704
Reputation: 36341
The typical approach to find memory leaks would be to create two or more memory snapshots/dumps and compare them with a memory debugger to see if there is some type of object that seem to only increase in count. I'm not sure stack traces would provide all that much value when trying to find memory leaks.
I have mostly used dotMemory but have no affiliation. I would expect most competent tools to have equivalent functionality, but terminology might be different.
There should be a comparison view where you should be able to sort object by either delta count or delta bytes . This often provides a good indication on what objects are leaking.
Once you have a candidate type you should be able to inspect an instance of that type and some way to view a retention graph where you can view the graph of objects keeping your instance alive. If you see anything suspicious, you should review the code and see if there is some possibility for leaks.
Common reasons are unregistered event handlers. If a object registers an event handler it should also ensure it is unregistered. If it is done in the constructor you should implement IDisposable and unregister in the dispose method, and ensure the object is always disposed. Possibly with a finalizer that only runs for undisposed objects, that logs, or otherwise alerts you, that the object was not disposed.
Upvotes: 2