【问题标题】:Blazor clear memory with IDisposableBlazor 使用 IDisposable 清除内存
【发布时间】:2021-01-13 21:41:52
【问题描述】:

我有一个异步加载数千行的 Blazor 页面。在 Visual Studio 中,我看到一旦加载此数据,我的 Blazor 应用程序会在内存中使用多个 gig。到目前为止没有问题。但是,换页时不会再次释放内存。

所以我实现了IDisposable,它取消了异步操作并将我的数据列表设置为空,但这似乎没有按预期工作。页面改变时调用Dispose方法,但内存消耗保持在1GB。

有人知道我做错了什么吗?

这是我的代码:

public List<ErloesKalkulatorServiceResult> Daten { get; set; } = new List<ErloesKalkulatorServiceResult>();

protected virtual void Dispose(bool disposing)
{
    if (!disposedValue)
    {
        if (disposing)
        {
            Daten = null!;
            cancellationToken?.Cancel();
        }

        // TODO: free unmanaged resources (unmanaged objects) and override finalizer
        // TODO: set large fields to null
        disposedValue = true;
    }
}

 ~ErloesKalkulator()
 {
     // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
     Dispose(disposing: false);
 }

public void Dispose()
{
    // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
    Dispose(disposing: true);
    GC.SuppressFinalize(this);
}

【问题讨论】:

  • 运行时不需要释放它所获得的内存,因为它会尝试将它重用于新的分配
  • 是的,但我不希望整个应用程序永久阻止此内存。因为所有其他页面仅使用几 MB。所以 1 场演出实在是太过分了。
  • 仅将集合设置为null 并不总是足够的。首先,当您调用Dispose 时,GC 不会释放资源。相反,它可以随时发生。 GC 可以访问实时 (JIT) 编译器和运行时维护的活动根列表。使用这个列表,GC 会创建一个图,其中包含所有可从根到达的对象。如果有单个实例维护对您的项目的强引用,并且该单个实例可从根访问,则无法收集该项目。
  • 一个例子是当项目订阅了一个可达实例的事件时。还有其他场景。在更改页面或调用Dispose 之后,您应该使用像 Visual Studio 调试工具这样的内存分析器来获取和检查当前堆中的快照,以查找占用内存的对象。分析器还会向您显示参考图。还要验证 Dispose 是否被实际调用。
  • 在您的情况下,不需要实现IDisposable。如果你理解了前面关于 GC 如何决定收集哪些对象的解释,你就会知道你只需要删除对拥有类型的所有引用(例如,从视图中删除页面,取消挂钩事件处理程序)就可以做到这一点实例及其所有引用的实例(子树)符合垃圾回收条件。但是当存在 GC 无法跟踪(管理)的非托管对象时,必须显式处置这些对象,以便让 GC 知道该实例(及其子树)是合格的。

标签: c# blazor dispose blazor-server-side


【解决方案1】:

我可以告诉你我在类似情况下做了什么。我的应用程序从数据库中获取数据,填充在 UI 中使用的列表。

所以在你的代码中,将 List.Clear() 放在页面的 Dispose 方法中。

然后运行垃圾收集器。它确实会减少堆大小。

最初,我没有使用 List.Clear(),而且在我看来确实永远不会从内存中清除数据。该应用程序的大小会增长,直到 IIS 接管并回收 AppPool,这会强制清除内存。我知道这样的内存应该被自动清除,但对我来说这是一个问题,因为它似乎正在泄漏内存。

但是,您必须使用 Visual Studio 中的诊断工具窗口来查看实际的堆大小。如果您在任务管理器中查看,您只会看到当前分配给它的整体内存量。所以它不可能缩小尺寸。但是,如果您查看诊断窗口并进行内存转储,您可以看到堆的实际当前大小,我敢打赌,它会告诉您对象实际上已被释放,即使应用程序没有放手的记忆。

请注意,除了像您这样的通常情况外,您不应手动调用 GC。

在下面的屏幕截图中,我使用 GC.Collect() 调用了 GC 进行测试。第二个快照显示减少的内存。我也在这里报告了这个:https://github.com/dotnet/aspnetcore/issues/39238

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-09
    • 1970-01-01
    • 1970-01-01
    • 2011-12-23
    • 2013-02-02
    • 2012-10-14
    • 1970-01-01
    相关资源
    最近更新 更多