Memory leak in Blazor Server-side project

huangapple go评论85阅读模式
英文:

Memory leak in Blazor Server-side project

问题

我创建了一个简单的系统来向 EF Core 数据库添加和删除文件,像这样:

  • 添加文件:
public async Task AddFile(IFileListEntry file)
{
    File fileToAdd = await File.GetFileAsync(file);
    _context.Files.Add(fileToAdd);
    _context.SaveChanges();
}
  • IFileListEntry 接口获取文件,并将其放入 Stream 中:
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;
}
  • 删除文件:
public void DeleteFile(File file)
{
    file.Data = new byte[0]; //Test
    _context.Files.Remove(file);
    _context.SaveChanges();
}

所有添加的文件都存储在一个 List 中,并从中删除,该列表本身包含在一个 .razor 文件中(未显示在此处,因为我认为它与我的问题无关),同时也存储在 EF Core 数据库中。

数据库上下文 _context 是通过依赖注入获取的,并继承自 DbContext 类。

所以我的问题是:在添加和删除文件后,我可以观察到内存泄漏,因为RAM在将文件添加到数据库和列表后保持在相同水平。

所以我到底做错了什么?我真的不明白可能出现的原因,因为 MemoryStream 在使用后已被处理,而当删除文件时,我将填充的 byte[] 替换为一个空数组,甚至是一个 null 引用。我确保我的 .razor 文件中的 List 是一个新的空列表,我们会再次填充其中添加的文件。但我还没有测试 _context

英文:

I made a simple system to add and delete file from an EF Core database like this :

  • Add a file :

     public async Task AddFile(IFileListEntry file)
     {
         File fileToAdd = await File.GetFileAsync(file);
         _context.Files.Add(fileToAdd);
         _context.SaveChanges();
     }
    
  • Get the file from an IFileListEntry interface, containing the file into a Stream :

     public static async Task&lt;File&gt; 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;
     }
    
  • Remove a file :

     public void DeleteFile(File file)
     {
         file.Data = new byte[0]; //Test
         _context.Files.Remove(file);
         _context.SaveChanges();
     }
    

All the files added are stored into a List, and deleted from it, and the list itself is contained into .razor file (not shown here as I think it's irrelavant to my problem), and also in an EF Core database.

The database context _context is obtained through depedency injection and inherits from DbContext class.

So my problem is : after adding and deleting a file, I can observe a memory leak as the RAM is at the same level after the addition of the file into the database and the list.

So what I'm doing wrong ? I really can't understand where it could possibly come from, as the MemoryStream is disposed after its use, and when the file is deleted, I replace the filled byte[] by an empty one, or even a null reference. I made sure the List in my .razor file is a new empty list that we fill up again with the files added. I didn't test for the _context though.

答案1

得分: 2

我认为你的DI容器没有释放dbcontext。

在这里查看dbcontext文档

上下文的生命周期始于创建实例时,当实例被释放或垃圾回收时结束。如果希望上下文控制的所有资源在块的末尾被释放,请使用using。使用using时,编译器会自动创建try/finally块,并在finally块中调用dispose。

public void UseProducts()
{
    using (var context = new ProductContext())
    {     
        // 使用上下文进行数据访问
    }
}

如果上下文实例是由依赖注入容器创建的,通常由容器负责释放上下文。

所以如果你的DI没有释放上下文,它将继续占用内存,因此看起来你有一个内存泄漏。

还有一件可能发生的事情是垃圾回收不会可预测地发生查看垃圾回收内容。因此,可能需要一些时间才能看到释放的内存。

最后一件事:你不需要 ms.Dispose(); MemoryStream

这种类型实现了IDisposable接口,但实际上没有任何需要释放的资源。这意味着不需要直接调用Dispose()或使用语言构造(如C#中的using或Visual Basic中的Using)来释放它。

所以MemoryStream不会是问题。

英文:

I think that your DI container does not dispose the dbcontext.

Check the dbcontext documentation here

> The lifetime of the context begins when the instance is created and ends when the instance is either disposed or garbage-collected. Use using if you want all the resources that the context controls to be disposed at the end of the block. When you use using, the compiler automatically creates a try/finally block and calls dispose in the finally block.

public void UseProducts()
{
    using (var context = new ProductContext())
    {     
        // Perform data access using the context
    }
}

> If the context instance is created by a dependency injection container, it is usually the responsibility of the container to dispose the context.

So if your DI does not dispose the context, then it remains in memory and thus, it looks like you have a leak.

Another thing that may occur, is that the Garbage Collection does not occur predictably check garbage collection here. So it might take some time to see the freed memory.

One last thing: You don't need to ms.Dispose(); MemoryStream

> This type implements the IDisposable interface, but does not actually have any resources to dispose. This means that disposing it by directly calling Dispose() or by using a language construct such as using (in C#) or Using (in Visual Basic) is not necessary.

So the MemoryStream would not be the problem.

huangapple
  • 本文由 发表于 2020年1月6日 23:26:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/59614710.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定