RDLC内存泄漏

问题描述 投票:0回答:1

在我的应用程序(.NET Framework 4.5)中,我正在渲染一些RDLC报告(50-60),以便将它们导出到单个PDF。

不幸的是,似乎存在大量内存泄漏,基本上每个LocalReportnever都会被处理掉。

这是我的代码:

public void ProcessReport(ReportDataSource[] reportDS, string reportPath)
{
    const string format = "PDF";
    string deviceInfo = null;
    string encoding = String.Empty;
    string mimeType = String.Empty;
    string extension = String.Empty;
    Warning[] warnings = null;
    string[] streamIDs = null;
    Byte[] pdfArray = null;

    using (var report = new LocalReport())
    {
        report.EnableExternalImages = true;
        report.ReportEmbeddedResource = reportPath;
        report.Refresh();

        foreach (var rds in reportDS)
        {
            report.DataSources.Add(rds);
        }
        report.Refresh();

        try
        {
            pdfArray = report.Render(format, deviceInfo, out mimeType, out encoding,
                out extension, out streamIDs, out warnings);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.InnerException.Message);
            throw;
        }

        report.ReleaseSandboxAppDomain();
        report.Dispose();

        //Add pdfArray to MemoryStream and then to PDF - Doesn't leak
    }
}

我发现内存泄漏只是通过查看Visual Studio内存面板,每次report.Render被称为添加20-30mb并且他们永远不会关闭直到我关闭应用程序。我确信使用MemoryStreamis不是问题,因为即使评论我仍然得到200mb-250mb的内存永远不会被释放。这很糟糕,因为在运行此应用程序3-4次后,它达到> 1GB,直到它甚至不再运行。我也尝试手动调用GarbageCollector但没有工作。该应用程序是32位。

我该怎么做才能解决这个问题?

c# .net rdlc
1个回答
4
投票

我有一个真正的解决方案,可以解释原因!

事实证明,LocalReport在这里使用.NET Remoting动态创建一个子appdomain并运行报告,以避免内部泄漏。然后我们注意到,最终报告将在10到20分钟后释放所有内存。对于生成大量PDF的人来说,这是行不通的。但是,这里的关键是他们正在使用.NET Remoting。 Remoting的关键部分之一是“租赁”。租赁意味着它将使Marshal对象保持一段时间,因为远程处理设置通常很昂贵,而且它可能不止一次使用。 LocalReport RDLC正在滥用此功能。

默认情况下,租赁时间是...... 10分钟!此外,如果有东西进行各种调用,它会增加2分钟的等待时间!因此,它可以随机地在10到20分钟之间,这取决于呼叫如何排队。幸运的是,您可以更改此超时发生的时间。不幸的是,您只能为每个应用程序域设置一次...因此,如果您需要除PDF生成以外的远程处理,您可能需要运行另一个服务,以便您可以更改默认值。要做到这一点,您需要做的就是在启动时运行以下4行代码:

    LifetimeServices.LeaseTime = TimeSpan.FromSeconds(5);
    LifetimeServices.LeaseManagerPollTime = TimeSpan.FromSeconds(5);
    LifetimeServices.RenewOnCallTime = TimeSpan.FromSeconds(1);
    LifetimeServices.SponsorshipTimeout = TimeSpan.FromSeconds(5);

您将看到内存使用开始上升,然后在几秒钟内您应该看到内存开始重新开始。花了很多时间用记忆分析器来真正跟踪它,并意识到发生了什么。

您不能将ReportViewer包装在using语句中(Dispose crashes),但是如果您直接使用LocalReport,则应该能够。在处理之后,你可以调用GC.Collect(),如果你想要更加确定你正在尽一切可能释放那些记忆。

希望这可以帮助!

编辑

显然,你应该在生成PDF报告后调用GC.Collect(0),否则看起来由于某种原因内存使用仍然会变高。

© www.soinside.com 2019 - 2024. All rights reserved.