We have an application that always grows in the amount of memory used. I have debugged this and found a memory leak, but cannot discover what is actually wrong with the code itself. I have used the "Make Object Id"-approach from this article to verify that the objects still exist long after the Razor page that generated them has disappeared.
This is the simplified code to illustrate:
//this is the only four lines of code in my Console app
await AsyncStuffProcessor.DoAsyncStuff();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
//and these are the classes
public class MyClass
{
public string Name { get; set; }
public int TypeId { get; set; }
public bool AsyncResult { get; set; }
}
public static class AsyncStuffProcessor
{
public static async Task<MyClass> DoAsyncStuff()
{
var model = new MyClass();
//we set some properties on the model class
model.Name = "some kind of name";
model.TypeId = 13;
//and pass the model to the ExecuteAsync method, which will perform async work based on values in the model class
var t1 = ExecuteAsync(model);
var t2 = ExecuteSomethingElseAsync();
Console.WriteLine($"T1: {t1.Id}");
await Task.WhenAll(t1, t2);
return model;
}
private static async Task ExecuteAsync(MyClass model)
{
//performs some async tasks, return when done,
//set a property in my model to the right value depending on the result
var result = await Task.Run<bool>(async () => { await Task.Delay(1000); return true; });
if (result)
model.AsyncResult = result;
}
private static Task ExecuteSomethingElseAsync()
{
//do some other async stuff
return Task.CompletedTask;
}
}
I place a breakpoint in my method DoAsyncStuff and use "Make Object ID" to name my reference to t1 as $1.
Printing $1 in the Immediate Windows in Visual Studio after the last GC.Collect() will still print my Task. The t1 still exists, even after forcing Garbage Collection.
Also, the ID of the t1 task always remains the same. If I execute my DoAsyncStuff() method in a loop, I would create hundreds of new Tasks named t1. Using the immediate window, I see that $1 is always the first task I created and this keeps on existing.
Why is this, and how can I avoid this?