on 09-08-2014 10:22 AM
Hi,
I am developing a Windows service. The service calls Crystal Report to generate PDFs. When a new contract appears in database (MS Access), the service reads contract number and build a formular. Then pass the formular to ReportDocument. The code looks like this
using (ReportDocument report = new ReportDocument()) {
report.Load("contract.rpt");
// some initialize report
foreach (var contract in contracts) {
report.RecordSelectionFormula = BuildFormular(contract);
report.ExportToDisk(ExportFormatType.PortableDocFormat, BuildOutputFilename(contract));
}
report.Close();
}
It works fine. When deploy on production, I want the location of Access file can be changed programmatically. So I modified the code
using (ReportDocument report = new ReportDocument()) {
report.Load("contract.rpt");
// some initialize report
foreach (ReportDocument subreport in report.Subreports) {
foreach (Table table in subreport.Database.Tables) {
table.Location = MdbPath;
}
}
foreach (Table table in report.Database.Tables) {
table.Location = MdbPath;
}
foreach (var contract in contracts) {
report.RecordSelectionFormula = BuildFormular(contract);
report.VerifyDatabase();
report.ExportToDisk(ExportFormatType.PortableDocFormat, BuildOutputFilename(contract));
}
report.Close();
}
It works but memory increases over time. Here are Private Bytes (I use Performance Monitor tool)
I also attached some screenshot of report of DebugDiag tool.
My environment:
Any suggestion?
Many thanks
Remove report.VerifyDatabase();
Not needed and only slows down the performance of the report engine.
Under report.Close();
add
report.Dispose;
You may want to see if adding GC.Collect will be of any use also.
- Ludek
Senior Support Engineer AGS Product Support, Global Support Center Canada
Follow us on Twitter
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Ludek,
I followed your advice. I removed VerifyDatabase(). Thanks. It make my program faster .
I added Dispose() right after Close(). Also added GC.Collect().
using (ReportDocument report = new ReportDocument()) {
report.Load("contract.rpt");
...
report.Close();
report.Dispose();
}
GC.Collect();
GC.WaitForPendingFinalizers();
Memory still increases steadily. Is there any thing else to do?
Now it would be interesting to see if this is CR or something else in the app causing the memory to keep going up. Note that this is the 1st time I hear of this issue in this version and SP of CR. Nevertheless, I'm open to it being CR - perhaps in connection with Access as Access is really not sued that much anymore.
Anyhow, I'd like you to take one of the reports - with saved data and run it in a loop. Do you see the memory increasing? I'd assume not, so move to next test;
Use a report that has no saved data - do not change the path to the Access database and again, loop through the report. Do you see memory increasing in this case?
The obvious 3rd test would now be - same as above, but change the path to the Access database.
Note that none of the above tests involve a service or populating of selection formulas.
- Ludek
Hi Ludek,
I start a new console project with a simple report. I enclosed VS solution and memory statistic.
The code is quite simple. The report do nothing but display the formular. I ran it for 50 minutes, the memory increase from 37.426MB to 43.267MB (~ 6MB). Do I miss any thing?
class Program {
static void Main(string[] args) {
List<String> policies = new List<string>() {
"00", "01", "02", "03", "04",
"05", "06", "07", "08", "09"
};
while (true) {
GenerateReportWithoutSubreport(policies);
System.Threading.Thread.Sleep(1000);
}
}
private static void GenerateReportWithoutSubreport(List<string> policies) {
using (var report = new ReportDocument()) {
report.Load("TestWithoutSubreport.rpt", OpenReportMethod.OpenReportByDefault);
foreach (string policy in policies) {
Console.WriteLine("Policy: " + policy);
foreach (FormulaFieldDefinition formula in report.DataDefinition.FormulaFields) {
if (formula.Name == "criterion") {
formula.Text = policy;
break;
}
}
var outputFilename = policy + ".pdf";
report.ExportToDisk(ExportFormatType.PortableDocFormat, outputFilename);
}
report.Close();
report.Dispose();
}
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
That was not quite the test I had in mind. I wanted a simple win app, looping through some x number of times and exporting the report.
E.g.;
For 1 to x ' (set x to say 100)
Load report
Export report to PDF
close report
dispose report
next x
Do the above with saved data report, then no saved data, then no saved data and changing the db path.
I do not want any connection to your service. E.g.; I want this to be a pure win CR app for now. This is the only way you will prove to me that CR is leaking memory.
- Ludek
Hi Ludek,
I wrote 3 tests. You can get detail here:
Here are summary:
Tests are a Windows Form apps with one main form which has one button. If we click on the button, it call Crystal Report to generate 100 pdf. The Access database is simple - one table which has 10 records.
Test case 1 - Saved data report.
I clicked 7 times (700 pdf created). Memory increase from 40.177 MB to 43.347 MB
Test case 2 - No saved data report
I clicked 10 times (1000 pdf created). Memory increase from 41.123MB to 42.835MB
Test case 3 - No saved data report and change location
I clicked 10 times (1000 pdf created). Memory increase from 52.412 to 54.063MB
Because I am developing a Windows Service and it runs 24/7, I expected that memory should not increase over time. My service runs 2 or 3 days and Crystal Report throws exception "Not enough memory for operation"
00:00:11,276 [Worker-8] ERROR e.PdfGenerator.PdfGeneratorJob - Error while generating pdf
CrystalDecisions.Shared.CrystalReportsException: Load report failed. ---> System.Runtime.InteropServices.COMException:
Not enough memory for operation.
at CrystalDecisions.ReportAppServer.ClientDoc.ReportClientDocumentClass.Open(Object& DocumentPath, Int32 Options)
at CrystalDecisions.ReportAppServer.ReportClientDocumentWrapper.Open(Object& DocumentPath, Int32 Options)
at CrystalDecisions.ReportAppServer.ReportClientDocumentWrapper.EnsureDocumentIsOpened()
--- End of inner exception stack trace ---
at CrystalDecisions.ReportAppServer.ReportClientDocumentWrapper.EnsureDocumentIsOpened()
at CrystalDecisions.CrystalReports.Engine.ReportDocument.Load(String filename, OpenReportMethod openMethod, Int16 parentJob)
at CrystalDecisions.CrystalReports.Engine.ReportDocument.Load(String filename, OpenReportMethod openMethod)
Thank for all advice
Vi
The problem here is that we have no idea who is actually leaking the memory. The Load Report error, only points to the fact that you ran out of memory, not that it is any CR file leaking. It could be a CR file, but it could also be any one of the many MS dependencies, printer driver, database client runtime, etc. One utility that may tell you who is leaking is Rational Purify. But note that tracing the source of the leak is not easy. E.g.; Purify may report a CR dll, but the actual leak may be happening in a VC++ dependency dll that the CR dll may be using. Additionally, minor leaks are notoriously hard to resolve and even if we proved the leak is due to a CR dll, getting it fixed will more than likely be a futile effort. So, what is a possible solution? recycle your service once every 24 hours(?). Another thing I'd like you to do on your reports is enable the "No Printer" option (File | Page Setup), this will decrease possible contributions to the mem leak from the printer driver.
Other than that, if after using something like Purify, you still think it is a CR issue, I'd recommend creating a phone support case here:
Crystal Single Case Technical Support - SAP Business Objects US Online Store | SAP Online Store
- Ludek
Hi Vi,
We do take memory leak seriously but as you can see below.... Cr is the least of problem...
I downloaded a trial version of Rational Purify and here's the results:
Purify for Windows,
(C) Copyright IBM Corporation. 1992, 2012. All Rights Reserved.
Version 7.0.1.0-003 Evaluation 32-bit ; Build: 20120705.2308;
Windows Server 2008 R2 Enterprise 6.1 7601 Multiprocessor Free
Instrumenting:
CrystalMemleak.exe 6144 bytes
Purify: While processing file C:\CrystalMemleak\bin\Release\CrystalMemleak.exe:
Warning: This native code executable statically links managed or mixed code module(s).
Purify does not support this configuration when inclusive instrumentation
is used.For more info, see the topic "Inclusive Instrumentation" in the Purify
online help.
NTDLL.DLL 1292192 bytes
ADVAPI32.DLL 640512 bytes
ADVAPI32.DLL 640512 bytes
MSCOREE.DLL 297808 bytes
MSVCRT.DLL 690688 bytes
Purify: While processing file C:\Windows\SysWOW64\MSVCRT.DLL:
Note: Instrumentation repeating with 7 additional entry points.
KERNELBASE.DLL 274944 bytes
SECHOST.DLL 92160 bytes
SECHOST.DLL 92160 bytes
KERNEL32.DLL 1114112 bytes
RPCRT4.DLL 663552 bytes
RPCRT4.DLL 663552 bytes
PCWUM.DLL 33280 bytes
PCWUM.DLL 33280 bytes
CRYPTSP.DLL 78848 bytes
CRYPTSP.DLL 78848 bytes
SSPICLI.DLL 96768 bytes
SSPICLI.DLL 96768 bytes
CRYPTBASE.DLL 36864 bytes
CRYPTBASE.DLL 36864 bytes
As you can see CR is leaking 6144 bytes
Nothing we can do about this, I doubt R&D will look for 6144 bytes...
Don
User | Count |
---|---|
93 | |
10 | |
10 | |
9 | |
9 | |
7 | |
6 | |
5 | |
5 | |
4 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.