LocalReport rendering causing OutOfMemory exceptions - c#

I have a WindowsService that converts XML files to a PDF via LocalReports (RDLC reports). It works very well and is fast, but I'm struggling with memory leak problems similar to those here.
It appears the .Render("PDF") method is where the problems are appearing. A profiling session in VS2015 shows a huge about of memory used by ServerIdentity. This doesn't appear if I comment out .Render("PDF").
Specs
Application is .NET 4.5
Application uses ReportViewer library from VS2012 (I've tried 2015, too, and same behavior)
Built for 'Any CPU', runs as x86 presumably
Reports aren't huge, but we run quite a few so after a few weeks memory gets used up (1 page reports, maybe a dozen or more a day).
Code
public static byte[] GenerateReport(string name, IEnumerable<ReportDataSource> dataSources)
{
try
{
byte[] pdfBytes;
using (Stream reportStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name))
{
LocalReport localReport = new LocalReport();
localReport.LoadReportDefinition(reportStream);
foreach (var dataSource in dataSources)
{
localReport.DataSources.Add(dataSource);
}
pdfBytes = localReport.Render("PDF");
//Cleanup!
localReport.DataSources.Clear();
localReport.ReleaseSandboxAppDomain();
localReport.Dispose();
}
return pdfBytes;
}
catch (Exception ex)
{
throw new Exception("Error generating report at " + name, ex);
}
}
Other notes
If I never call .Render("PDF"), the memory never grows beyond 20MB or so.
Similarly, if i never render, there's no ServerIdentity or Microsoft.ReportingServices.OnDemanProcessing.*` objects in the heap. This is where 90%+ of the memory is consumed but I have no way to GC that memory.

Change the compilation target of your application to x64 and call
report.ReleaseSandboxAppDomain();
After Render method. Remember to Dispose the report object.

Related

LocalReport hangs sometimes on render

When generating a RDLC report in a C#/WPF application, sometimes it hangs on render (call of render method doesn't return).
The report data comes from a C# object (which is filled in advance with data from a PostgreSQL database) so this issue is not SQL related.
I wonder:
How can I find out the reason?
Is there any way to enable the user to kill the report generation when it hangs?
Embedding the report generation in a using block (as seen in ReportViewer rendering hangs server after some executions) didn't solve the problem.
using (LocalReport report = new LocalReport())
{
report.ReportPath = #"C:\Path\To\MyReport.rdlc";
// Get the report data from db and fill it into MyReportData object.
MyReportData data = CreateReportData();
ReportDataSource headerDataSource = new ReportDataSource();
headerDataSource.Name = "HeaderDataSet";
headerDataSource.Value = data.Header;
report.DataSources.Add(headerDataSource);
// Add more data sources ...
// No report parameters are being used
report.Refresh();
pdfData = report.Render("PDF"); // here it hangs sometimes
}
As said above, the render-call hangs sometimes. After killing the application and generating exactly the same report again it works.

Emgu cv high resolution images stitching Issue

I am using EmguCV library to stitch images. Its working fine for small images but getting exception when processing high resolution images or images above 20MB size or even if I try to process more than 30 images it fails.
Libraries I am using
Emgu.CV.UI
Emgu.CV.UI.GL
Emgu.CV.World
opencv_core2410
opencv_imgproc2410
Code
List<Image<Bgr, Byte>> sourceImages = new List<Image<Bgr,byte>>();
foreach (string path in ImgPath)
sourceImages.Add(new Image<Bgr, Byte>(path));
using (Stitcher stitcher = new Stitcher(false))
{
using (VectorOfMat vm = new VectorOfMat())
{
Mat result = new Mat();
vm.Push(sourceImages.ToArray());
stitcher.Stitch(vm, result);
if (result.Bitmap != null)
{
result.Bitmap.Save(Application.StartupPath + "\\imgs\\StitchedImage.png");
}
else
{
MessageBox.Show("Some thing went wrong"); return null;
}
}
}
Exception
((Emgu.CV.MatDataAllocator)(result))._dataHandle.Target' threw an exception of type 'System.InvalidOperationException
Image
I was fairly certain that you are running into a memory issue and so I went ahead and made a simple console app targeting .Net 4.7.2, using the latest EmguCV package from NuGet, which is version 3.4.3.3016, and using the code below on sample data from Adobe, which can be downloaded here. If I compile as "AnyCPU" with "prefer 32 bit" and run this code against the rio image set (I loaded up the png's for the test purposes) and let it run the memory will slowly jump up until it hits close to 3 GB and then shortly thereafter crashes giving the exception about the refcount. Pretty clearly a memory issue. I then recompiled targeting 64 bit and was able to successfully run the code. The memory usage peaked out around 6 GB. So, with that in mind, I would be fairly certain that your issue is also memory related. You have yet to answer the question on whether you are building a 64 bit app or not, but based on what you are seeing, I would guess that you are not. So, the solution to your problem is to compile as 64 bit and then be sure you have enough memory. With the rio test set it jumped up to close to 6 GB. Without having your images I can't tell you how large it might grow, but these type of operations are pretty memory intensive - so more is better. This would explain both the issue with large image files and the issue with a large number of small image files. I was able to successfully process sets of images that were between 10 and 20 images using a 32 bit build, but as soon as I moved to the 50+ image sets it would only work with a 64 bit build due to the memory requirements.
var images = Directory.EnumerateFiles(#"C:\test\adobe\rio", "*.png", SearchOption.TopDirectoryOnly).Select(x => new Mat(x)).ToArray();
using(var stitcher = new Stitcher(false))
{
using (var vm = new VectorOfMat(images))
{
var result = new Mat();
stitcher.Stitch(vm, result);
result.Bitmap.Save(#"C:\test\adobe\rio_stitched.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
result.Dispose();
}
}
foreach (var image in images) { image.Dispose(); }

RDLC Memory Leak

In my application (.NET Framework 4.5) I'm rendering some RDLC reports (50-60) in order to export them to a single PDF.
Unfortunately there seems to be a big memory leak, basically every LocalReportnever gets disposed.
This is my code:
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
}
}
I found the memory leak just by looking to Visual Studio memory panel, every time report.Render get's called it add 20-30mb and they never go down until I close the application. I'm sure that using the MemoryStreamis not the issue because even if commented I still get 200mb-250mb in memory that never get released. This is bad because after running this application like 3-4 times it reaches >1GB until it doesn't even run anymore. I also tried to manually call the GarbageCollector but didn't work. The application is 32 bit.
What can I do to fix this ?
I have a real solution and can explain why!
It turns out that LocalReport here is using .NET Remoting to dynamically create a sub appdomain and run the report in order to avoid a leak internally somewhere. We then notice that, eventually, the report will release all the memory after 10 to 20 minutes. For people with a lot of PDFs being generated, this isn't going to work. However, the key here is that they are using .NET Remoting. One of the key parts to Remoting is something called "Leasing". Leasing means that it will keep that Marshal Object around for a while since Remoting is usually expensive to setup and its probably going to be used more than once. LocalReport RDLC is abusing this.
By default, the leasing time is... 10 minutes! Also, if something makes various calls into it, it adds another 2 minutes to the wait time! Thus, it can randomly be between 10 and 20 minutes depending how the calls line up. Luckily, you can change how long this timeout happens. Unluckily, you can only set this once per app domain... Thus, if you need remoting other than PDF generation, you will probably need to make another service running it so you can change the defaults. To do this, all you need to do is run these 4 lines of code at startup:
LifetimeServices.LeaseTime = TimeSpan.FromSeconds(5);
LifetimeServices.LeaseManagerPollTime = TimeSpan.FromSeconds(5);
LifetimeServices.RenewOnCallTime = TimeSpan.FromSeconds(1);
LifetimeServices.SponsorshipTimeout = TimeSpan.FromSeconds(5);
You'll see the memory use start to rise and then within a few seconds you should see the memory start coming back down. Took me days with a memory profiler to really track this down and realize what was happening.
You can't wrap ReportViewer in a using statement (Dispose crashes), but you should be able to if you use LocalReport directly. After that disposes, you can call GC.Collect() if you want to be doubly sure you are doing everything you can to free up that memory.
Hope this helps!
Edit
Apparently, you should call GC.Collect(0) after generating a PDF report or else it appears the memory use could still get high for some reason.

PDFClown System.OutOfMemoryException while populating large file

I am generating a large Report pdf file using PDFClown using data from a database.
The process takes a very long time and eventually runs out of memory when the number of pages nears the 150 mark taking up more than 1.5GB of ram and with the error:
A first chance exception of type 'System.OutOfMemoryException' occurred in PDFClown.dll
Since I will need to regularly generate reports of over 1500 pages this is a major problem.
Is there anything I can do to:
Not run out of memory (Necessary)
Speed up the file creation (Ideally)
Please note: the reports generated (with smaller data sets) are accurate, although the file size is rather large.
Here is a sample of my code:
protected void PopulateReport()
{
foreach (Page page in _lstPages)
{
if (page != _Titlepage)
{
PrimitiveComposer composer = new PrimitiveComposer(page);
BlockComposer blockComposer = new BlockComposer(composer);
DataRow drInspection;
if (_mapPage1Rows.TryGetValue(page, out dataRow))
{
GeneratePage1(page, composer, blockComposer, dataRow);
}
else if (_mapPage2Rows.TryGetValue(page, out dataRow))
{
GeneratePage2(page, composer, blockComposer, dataRow);
}
}
}
}
protected void GeneratePage1()
{
composer.BeginLocalState();
composer.SetFont(ReportFonts.GetFont(GetPDFDocument(), bIsFieldName, false), nFontSize);
blockComposer.Begin(GetRectangle(fMarginX, fMarginY, fWidth, nFontSize), AlignX, AlignY);
int nIndex = blockComposer.ShowText(strText, false);
blockComposer.End();
....
composer.End();
composer.Flush();
}
Screenshot Sample Report Page (redacted for client-privacy reasons):
The Function: ReportFonts.GetFont(...) was creating a new font every single time it was called.
This font was then stored in the dll's memory space and the final file which was what was taking up so much space.
Used a Dictionary<> to solve the issue, not only is the memory space clean and the file size acceptable, but the execution time is also much improved.
Moving to 64-bit also helped somewhat.

Printing multiple report using Crystal Report crash applcation

I have a WinForms Application (.net 4.0, x86) with Crystal Reports (13.0.15). I want to print multiple documents using Crystal Reports (without seeing them on Report Viewer - directly on printer).
foreach (var document in documents )
{
ReportDocument report=GenerateReport(document.id);
report.PrintToPrinter(printerSettings, pageSettings, false);
}
Everytime it prints 48 reports and on 49th print I've got exception System.ComponentModel.Win32Exception The handle is invalid Void OnStartPrint(System.Drawing.Printing.PrintDocument, System.Drawing.Printing.PrintEventArgs)
I tried another set of documents but I still have exception on 49 print.
I tried to change PrintJobLimit to over 9000 but it doesn't help.
Next I tried to dispose report after print:
foreach (var document in documents )
{
using (ReportDocument report=GenerateReport(document.id))
{
report.PrintToPrinter(printerSettings, pageSettings, false);
}
}
But now program crashes before print 28th report and I cannot catch exception (program stop working). Only in Event Viewer is information about Application Error (Faulting module name: ntdll.dll).
I tried different things (and combination of them):
report.Close();
report.Dispose();
report=null;
even GC.Collect() after print but it still doesn't work.
Anybody has a solution?
It seems like when report.PrintToPrinter returned, the underlying operation sometimes went on. You need to keep reportDocument alive for a few more time, instead of let it go out of scope and GC collection kick in.
My solution is to put them into a delayed queue and Close them later, something like:
foreach (var document in documents )
{
ReportDocument report = GenerateReport(document.id);
report.PrintToPrinter(printerSettings, pageSettings, false);
Task.Run(async () => {
// keep report alive for extra 60 secs
await Task.Delay(60 * 1000);
report.Close();
report.Dispose();
})
}

Categories