Is this a memory leak?
Recently I’ve run into an issue where a memory leak was reported when Crystal Reports were being run in a VS .NET application. The description of the issue was along the lines of:
We are running a report in .NET using the following code:
Dim crReportDocument As New CrystalDecisions.CrystalReports.Engine.ReportDocument()
CrystalReportViewer1.reportSource = crReportDocument
When the report is loaded we see memory increase by about 40 MB, which is expected. However on .Close and .Dispose we do not see all of that memory recovered. If we run several reports, one after another, we see memory increase, then decrease as the reports get done, but we never recover the initial memory assignment. On close of the application, the memory is recovered.
Couple of things when reporting suspected memory issues. It is always good to know what utility / tool is being used to track the memory usage. E.g.; Performance Monitor, Windows Task Manager, or? It is also useful to know what is being tracked; Private Bytes? Handle Count? Mem Usage? Other?
The simple answer to this case is that this is not a memory leak as such. Looking for a definition of memory leak:
[https://en.wiktionary.org/wiki/memory_leak | https://en.wiktionary.org/wiki/memory_leak]
The above states: “Any of several faults in a personal computer’s memory allocation logic whereby parts of memory become unusable or hidden.”
[https://searchwinit.techtarget.com/sDefinition/0,,sid1_gci213633,00.html | https://searchwinit.techtarget.com/sDefinition/0,,sid1_gci213633,00.html] sates; “A memory leak is the gradual loss of available computer memory when a program (an application or part of the operating system) repeatedly fails to return memory that it has obtained for temporary use.”
Obviously, in the case considered in this blog, the above is not the case as memory used remains stable.
Can this chunk of memory be recovered during the running of the application?
This behavior is by design, and is actually quite important. An application using Crystal Reports, more then one report can be loaded at one time thus the underlying dlls couldn’t be unloaded until all reports for that application were closed. Often, one report is loaded right after another. If the Crystal Reports engine unloaded all the dlls once a report is completed, then every time a new report was opened, all the core dlls and assemblies would need to re-loaded over and over and over. As it is, I see many requests for improving performance on first load of a report. See the article
“Improving Crystal Reports Performance in Visual Studio .NET Applications” for more details. In addition, all optional dlls would need to be reloaded and all registry keys would need to be read over again. This would typically lead to an unacceptable performance hit in the speed of the reports being viewed and possibly on the entire system. By leaving the core engine and dlls loaded after the first report is closed, there is no longer this “bottle neck” and typically, loading when loading subsequent reports the performance improves dramatically.
In general this is not an issue for Windows applications as it is usually one user on a system at a time and 40 MB for a user is not much. For web applications this can be a potential issue -depending on the number of users accessing your application. With several tens of users, this should be manageable. However if there are 100s of users accessing the application it is likely that Crystal Reports may not be the best solution. Look at more scalable solutions such as Crystal Reports Server. Or possibly Business Objects Enterprise where you can offload the report generation from your Web App server to the Enterprise server to see if this would be of benefit. For more information regarding scalability and performance see the the article “Crystal Reports 2008 Component Engine Scalability“.
Other useful resources:
Choosing the Right Business Objects SDK for Your Needs
Crystal Reports Developer Advantage runtime license
Crystal Reports Licensing
Crystal Reports XI Release 2 Component Licensing Explained