I have noticed, that the MiniProfiler somehow doesn't dispose/release the Timings leading to a possible memory leak.
I used the DotMemory of Jetbrains to make a snapshot before and after the workload. All of the 1000 Timings still remained in an instance of the MiniProfiler, which i cannot seem to get rid of.
This could lead quite some problems of, for example, someone would profile his database queries which could have quite some length to them.
Minimal-example:
public static void Main()
{
try
{
//Snapshot here
Console.ReadKey();
var mp = MiniProfiler.StartNew();
var rnd = new Random();
for (int j = 0; j < 1000; j++)
{
using (mp.Step("outer"))
{
rnd.Next();
}
}
Console.WriteLine(MiniProfiler.Current.RenderPlainText());
mp.Stop(true);
mp = null;
if (Debugger.IsAttached)
Console.ReadKey();
//Snapshot here
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
Thanks for any help!
Edited my question for one more example. Thanks @Ed Pavlov, but as soon as I create a bigger example the Timings will still remain. Next example was build with Release Config and executes a sql query against a database. (I deliberately choose a quite big command text to demonstrate the phenomenon)
public static async Task Main()
{
try
{
Console.ReadLine();
await DoStuff();
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
internal static async Task DoStuff()
{
var mp = MiniProfiler.StartNew("Test");
for (int i = 0; i < 5000; i++)
{
await Test();
if (i % 200 == 0) Console.WriteLine(i);
}
Console.WriteLine(MiniProfiler.Current?.RenderPlainText());
await mp.StopAsync(true);
}
public static async Task Test()
{
var rnd = new Random();
var txt = CommandFacade.CommandText.Replace("@@ID@@", rnd.Next(3000).ToString());
using (MiniProfiler.Current.Step("Level 1"))
using (var conn = GetConnection())
using (var cmd = new ProfiledDbCommand(new SqlCommand(txt), conn, null))
{
await conn.OpenAsync();
await cmd.ExecuteNonQueryAsync();
conn.Close();
}
}
public static DbConnection GetConnection()
{
DbConnection cnn = new SqlConnection(ConnectionString);
if (MiniProfiler.Current != null)
{
cnn = new ProfiledDbConnection(cnn, MiniProfiler.Current);
}
return cnn;
}
Picture of dotMemory with new example:
DotMemory Overview Screenshot
Screenshot of retained object