C# Blazor服务器端项目中的内存泄漏
我制作了一个简单的系统,可以从EF Core数据库中添加和删除文件,如下所示:C# Blazor服务器端项目中的内存泄漏,c#,asp.net,entity-framework,blazor-server-side,C#,Asp.net,Entity Framework,Blazor Server Side,我制作了一个简单的系统,可以从EF Core数据库中添加和删除文件,如下所示: 添加一个文件: public async Task AddFile(IFileListEntry file) { File fileToAdd = await File.GetFileAsync(file); _context.Files.Add(fileToAdd); _context.SaveChanges(); } 从IFileListEntry界面获取文件,将文件包含在流中: p
- 添加一个文件:
public async Task AddFile(IFileListEntry file) { File fileToAdd = await File.GetFileAsync(file); _context.Files.Add(fileToAdd); _context.SaveChanges(); }
- 从
界面获取文件,将文件包含在IFileListEntry
:流中
public static async Task<File> GetFileAsync(IFileListEntry file) { using MemoryStream ms = new MemoryStream(); await file.Data.CopyToAsync(ms); File f = new File { Name = file.Name, Type = file.Type, Size = file.Size, LastModified = file.LastModified, Data = ms.ToArray(), }; ms.Dispose(); return f; }
列表中
,并从中删除,列表本身包含在.razor
文件中(此处未显示,因为我认为这与我的问题无关),也包含在EF核心数据库中
数据库上下文\u context
通过依赖注入获得,并从DbContext
类继承
所以我的问题是:在添加和删除文件后,我可以观察到内存泄漏,因为在将文件添加到数据库和列表中后,RAM处于相同的级别
那我做错了什么?我真的不明白它可能来自哪里,因为
MemoryStream
在使用后会被处理掉,当文件被删除时,我会将填充的byte[]
替换为一个空的,甚至是一个null
引用。我确保我的.razor
文件中的列表是一个新的空列表,我们用添加的文件再次填充该列表。但是我没有测试\u上下文。我认为您的DI容器没有处理dbcontext
上下文的生存期从实例创建时开始,到实例被释放或垃圾回收时结束。如果希望上下文控制的所有资源都放在块的末尾,请使用。使用using时,编译器会自动创建try/finally块,并在finally块中调用dispose
如果上下文实例是由依赖项注入容器创建的,则通常由容器负责处理上下文
因此,如果您的DI没有处理上下文,那么它将保留在内存中,因此,看起来您有泄漏
另一件可能发生的事情是,垃圾收集不会以可预测的方式发生。因此,可能需要一些时间才能看到释放的内存
最后一件事:你不需要ms.Dispose()代码>
此类型实现IDisposable接口,但实际上没有任何可处置的资源。这意味着不需要通过直接调用Dispose()或使用语言构造(如使用(在C#)或使用(在Visual Basic中)来处理它
因此,MemoryStream
不会成为问题。“添加和删除文件后,我可以观察到内存泄漏,因为在将文件添加到数据库和列表后,RAM处于相同的级别。”这听起来不像内存泄漏。这就是.NET内存堆的工作原理。我并没有指出Blazor或ASP.NET框架的内存泄漏。当然,我只是想弄清楚这个未使用的、可能是无用的分配内存被保存在哪个变量中,以及如何处理它,因为在上传一个映像后,有时可能需要100MB的RAM,因此,一些东西可能会很快导致RAM被数百次上传完全填满@athanasios kataras确实回答了我的问题,我将尝试手动处理上下文以清除这些未使用的内存。感谢您的回答,它似乎符合逻辑,因为它来自未被处理的DbContext
。我真的不明白为什么DI不处理它。因此,解决方案是手动处理它,还是使用using
语句?知道我是通过DI获得这个上下文的,我如何应用其中一个解决方案呢?(很抱歉回答晚了,我刚在下班前发布了此消息)取决于DI。例如,在netcore中,AddDbContext扩展方法默认使用作用域生存期注册DbContext类型。因此,默认情况下处理处置。这取决于您正在使用的DI实现。我的DI实现是您在创建简单的Blazor服务器端项目和标识脚手架后获得的,因此上下文注入在脚手架IdentityHostingStartupClass:IHostingStartup
类中处理,如下所示:builder.ConfigureServices((context,services)=>{services.AddDbContext(options=>options.UseSqlServer(context.Configuration.GetConnectionString(“…”))
经过更多的测试,我发现了非常奇怪的事实:在一定数量的上传之后,GC似乎完成了他的工作,RAM稳定在大约400MB的值,尽管我仍然不明白为什么。同样的,如果你点击导航栏标签,RAM会不断增加,直到GC收集到一个点,然后RAM保持稳定。我真的不明白下面的“魔力”,但至少它似乎可以通过“自身”避免内存泄漏。再次感谢您的帮助。干杯!似乎这是一些GC魔力。您用于DI的方法正确地处理了上下文,所以您应该没事!
public void DeleteFile(File file)
{
file.Data = new byte[0]; //Test
_context.Files.Remove(file);
_context.SaveChanges();
}
public void UseProducts()
{
using (var context = new ProductContext())
{
// Perform data access using the context
}
}