我注意到,MiniProfiler在某种程度上没有释放/释放可能导致内存泄漏的时间。
我使用Jetbrains的DotMemory在工作负载前后进行快照。所有1000个时间仍然保留在MiniProfiler的一个实例中,我似乎无法摆脱这个实例。
这可能会导致相当多的问题,例如,某些人会分析他的数据库查询,这些查询可能有相当长的时间。
最低限度-例如:
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);
}
}谢谢你的帮助!
又一个例子编辑了我的问题。谢谢@ example,但是一旦我创造了一个更大的例子,时间仍然会继续。下一个示例是使用Release构建的,并针对数据库执行查询。(我特意选择了一个相当大的命令文本来演示这一现象)
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;
}新示例显示的dotMemory图片:
发布于 2022-06-14 17:52:57
尽管当应用程序在"Debug“配置中构建w/o优化时,将变量"mp”设置为null,编译器仍然生成将所有对象保存到方法末尾的代码。正如您在屏幕截图中所看到的,MiniProfiler和所有定时实例都由局部变量保存在内存中。
在“释放”配置中构建应用程序,或者更好地将代码提取到单独的方法中,以便更好地模拟真实的代码,在方法调用之后,您将看到内存中没有这些对象。
public static void Main()
{
//Snapshot here
Console.ReadLine();
Isolate();
Console.WriteLine("Ready");
Console.ReadLine();
//Snapshot here
}
private static void Isolate()
{
try
{
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;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}

发布于 2022-07-07 01:22:24
我对此的再现表明,异步/等待的使用是捕获执行上下文,它保持对MiniProfiler内部使用的MiniProfiler字段内的值的引用。
问题不是缺乏处理,也不是MiniProfiler没有释放时间。Timing只是一次性的,这样就可以方便地限定作用域了。实际的Timing.Dispose方法只是Timing.Stop。
查看顶级的Timing实例,会发现它被保存在ExecutionContext上,当我进一步挖掘时,似乎是由于围绕着SQL方法的异步/等待而被实例化了。当我切换到同步等价物时,所有的时间都会得到GC,用一个简单的await Task.Delay(1)替换SQL逻辑也会复制原来的问题。
我稍微打探了一下,如果MiniProfiler做了类似于这里的操作,这看起来可能会有所缓解,但我不知道这个问题有多大,因为我的测试表明,对DoStuff的许多调用不会增加剩余的内存消耗,因为只保留了最后一个分析器。
(用.NET 6和MiniProfiler 4.2.55测试)
https://stackoverflow.com/questions/72615050
复制相似问题