Just recently, after deploying a web application to a testing environment, I was approached by one of the testers complaining of an “Out of Memory” exception.
However, some areas required developing from the ground up. One of these areas was the code that XML-formatted the data, and performed the XSLT transforms upon it. A particular headache was to get the whitespace recognition and treatment on the .NET platform to work the same as the older JSP system. After hours of trying different settings, in the end I was forced to reformat the underlying XSLT file just to get the screens looking the same. However, I digress…
With most of the major hurdles out-of-the-way, and the application delivered to testing, it was quite a disappointment to find a memory leak as the cause of the “Out of Memory” exception, especially at the scale I was noticing (50K+ per web page post).
I was pretty sure I had cleaned up unmanaged resources as required, but set about my code with a fine tooth comb to see if there was anything I missed. Eventually I fired up perfmon and stepped through the code line-by-line, looking for where significant memory consumption occurred.
I happened to notice significant memory consumption when stepping over the code that loaded the XSLT and performed the transformation (the equivalent to lines 5 and 10 in the sample below)
var xslCompiledDoc = new XslCompiledTransform(true);
//....get myXSLDoc from some XSL file
//....get xmlSource and instantitate an XmlWriter
//Memory leak on this line!!!
This restored my ego a little. It looked like the problem might be some side-effect of XslCompiledTransform, and not the result of overly crap code. It also meant that I had pinpointed where exactly the problem lay, even if I didn’t know how to solve it.
As it turned out, no one solution solved the problem, but it did lay bare an interesting problem with the System.Xml.Xsl.XslCompiledTransform .NET class. When executed, this class will take the XSLT that is passed to it, and compile it down to IL. The problem was that when the client application was run in debug mode, both the load and transformation leaked memory.
So here are the steps that I took to seal the leak:
1. XMLWriter Cleanup
Although the leak wasn’t all my fault, I may have been partially responsible. My first action was to correctly ensure that the XmlWriter unmanaged resource wrapper was cleaned up correctly:
using (XmlWriter xmlWriter = XmlWriter.Create(xmlStringBuilder, xmlWriterSettings))
Using using in this context is considered all-round good practice for any class that wraps managed resources and implements IDisposable.
2. Debug Option Off
The first overload of the XslCompiledTransform constructor accepts a boolean parameter indicating whether or not to enable debug information, and therefore be able to debug the stylesheet itself.
You’ll note from my original code that I had this option set to true, which caused the load and transform to leak memory. The fix was to pass false explicitly, although I could have used the default parameterless constructor instead.
3. Caching in Session
The XslCompiledTransform class was designed to be initialised once (i.e. one call to the load function), and for that instance used for the duration of the application.When you think about it, there is little point in performing a compilation of XSLT to IL every occasion, unless the XSLT content itself is variable.
My code contained two static XSLT files, so I chose to cache these stylesheets in session once initialised. Technically, they could have been stored at application level once and their one instance re-utilised for every user. I must confess that this is not an option I considered at the time, but is something I can always revisit if needs be.
4. Release Mode DLL
The new version of the application was to be hosted within existing application infrastructure. Essentially, the port of the old application was to be a new web page within an existing website.
This existing website had been deployed in debug mode. Changing every module of a large website to release mode was not an option, so I moved all the supporting code out into a dedicated DLL that itself could be deployed in release mode on its own.
5. Precompiling XSL
After doing all this however, I was still observing a significant memory leak upon each page post (between 10K and 13K). The memory leak was now reserved to the transform, but it was still occurring.
The last step, which eventually fixed the issue was to follow the advice set out at the end of this article:
Another way to resolve the problem is to compile the stylesheet “offline” (i.e. not at runtime) with the xsltc (xslt compiler, more details in MSDN http://msdn.microsoft.com/en-us/library/bb399405.aspx). This tool will create “regular” assemblies (.dll files) for the Xslt stylesheet and the embedded scripts. To be able to use them in your app you just need to add references. The memory leak problem is resolved – there is no compilation going on at runtime so no temporary assemblies are created.
At the time of writing this was required to be a manual build step. The actions that I tool specifically were to compile each of my XSLT files (input.xsl and output.xsl) into DLLs with equivalent names (InputXSL.dll and OutputXSL.dll) and internal class names (InputXSL and OutputXSL). This was done with the following syntax:
xsltc.exe /class:InputXSL /out:InputXSL.dll input.xsl
Note: the xsltc.exe executable took some finding. The one I needed was located in C:/Program Files/Microsoft SDKs/Windowsv7.0A/bin, however this will be dependent on the .NET framework version you are using. If you don’t have the framework SDK installed then you will need to do so.
Once the DLLs were compiled, I referenced them from the new component I created in step 4 above. This gave the code access to the internal class names just stipulated.
Finally, I updated the code itself to pass the respective stylesheet class to the XsltCompiledTransform load() function:
When this last step was completed, the memory profile of the application remained constant, and there were no observable memory leaks.