RDLC内存泄漏

时间:2018-11-09 16:35:14

标签: c# .net rdlc

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

不幸的是,似乎有很大的内存泄漏,基本上每个LocalReport都没有被丢弃。

这是我的代码:

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,直到我关闭应用程序,它们才会崩溃。我敢肯定,使用MemoryStream并不是问题,因为即使发表评论,我仍然会得到200mb-250mb的内存,这些内存永远不会释放。这很糟糕,因为在运行该应用程序大约3-4次后,它达到了1GB以上,直到不再运行为止。我也尝试手动调用GarbageCollector,但是没有用。该应用程序是32位。

该如何解决?

1 个答案:

答案 0 :(得分:4)

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

事实证明,这里的LocalReport使用.NET Remoting动态创建子appdomain并运行报表,以避免内部泄漏。然后,我们注意到,最终,该报告将在10到20分钟后释放所有内存。对于生成大量PDF的人来说,这是行不通的。但是,这里的关键是他们正在使用.NET Remoting。远程处理的关键部分之一就是所谓的“租赁”。租赁意味着它将保留该元帅对象一段时间,因为设置远程处理通常很昂贵,并且可能会多次使用。 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);

您将看到内存使用率开始上升,然后在几秒钟之内应该看到内存使用率开始下降。用内存分析器花了我几天时间来真正跟踪下来并了解发生了什么。

您不能在using语句中包装ReportViewer(Dispose崩溃),但是如果直接使用LocalReport,您应该可以。处理之后,如果您想再次确定自己正在尽一切努力释放该内存,则可以调用GC.Collect()。

希望这会有所帮助!

编辑

显然,您应该在生成PDF报告后调用GC.Collect(0),否则由于某些原因,看来内存使用量仍然可能很高。