PRB: Cannot unload assemblies that you create and load by using script in XSLT

Article translations Article translations
Article ID: 316775 - View products that this article applies to.
This article was previously published under Q316775
This article has been archived. It is offered "as is" and will no longer be updated.
This article refers to the following Microsoft .NET Framework Class Library namespaces:
  • System.IO
  • System.Xml
  • System.Xml.XPath
  • System.Xml.Xsl
Expand all | Collapse all

On This Page

SYMPTOMS

When you use the <msxsl:script> element repeatedly with System.Xml.Xslt framework, a memory leak may occur with a high volume Extensible Markup Language (XML) or Extensible Stylesheet Language (XSL) application.

CAUSE

The classes of the System.Xml namespace support the use of embedded scripting by using the script element in XSL Transformation (XSLT) applications. Therefore, the <msxsl:script> element allows you to choose the programming language (such as Microsoft Visual C# .NET or Microsoft Visual Basic .NET) to perform certain tasks.

Declared functions are contained within script blocks. When you use embedded script with an XSL file, an assembly that contains Microsoft Intermediate Language (MSIL) is created and loaded into memory. Because of a design limitation in this version of the Microsoft .NET Framework, you cannot unload that assembly from memory. This may lead to a memory leak if assemblies are created and loaded repeatedly or in a loop.

RESOLUTION

To resolve this problem, do not repeatedly load the XSLT with the script. Develop your application in such a way that you load the XSLT once and reuse it as many times as needed. This practice also improves performance.

For example, the following code leaks memory:
For(int i=0;i<1000;i++)
{
      xslt.Load(stylesheet);
      //Do other stuff
      xslt.Transform(doc, null, writer);
}
				
Change the code as follows to load XSLT only once and reuse it in a loop:
xslt.Load(stylesheet);
For(int i=0;i<1000;i++)
{

      //Do other stuff
      xslt.Transform(doc, null, writer);
}
				
This code only loads the assembly once and does not leak memory.

WORKAROUND

Unload an individual assembly by unloading all the application domains that contain the assembly. To do this, call the AppDomain.Unload() method for each application domain that has the assembly loaded, or call the UnloadDomain() method on the unmanaged hosting API.

MORE INFORMATION

Steps to reproduce the behavior

  1. Create a Visual C# .NET Console Application project.
  2. Create an XSLT application, and then add the following code:
    using System;
    using System.IO;
    using System.Xml;
    using System.Xml.XPath;
    using System.Xml.Xsl;
    
    public class Sample
    {
    	private const String filename = "..\\..\\XmlFile1.xml";
    	private const String stylesheet = "..\\..\\XSLTFile1.xslt";
    
    	public static void Main() 
    	{
    
                    for(int i=0;i<1000;i++)
                    {
    
    		XslTransform xslt = new XslTransform();
                    xslt.Load(stylesheet);
    
    		//Load the XML data file.
    		XPathDocument doc = new XPathDocument(filename);
    
    		//Create an XmlTextWriter to write to the console.         
    		XmlTextWriter writer = new XmlTextWriter(Console.Out);
    		writer.Formatting = Formatting.Indented;
    
    		//Transform the file.
    		xslt.Transform(doc, null, writer);
    		writer.Close();
                    }
    		System.Console.Read();
            
    	} 
    }
    					
  3. Create an XSLT file that is named XSLTFile1.xslt, and then add the following code:
     <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:msxsl="urn:schemas-microsoft-com:xslt"
        xmlns:user="urn:my-scripts">
    
      <msxsl:script language="C#" implements-prefix="user">
    
    <![CDATA[
    
         public double circumference(double radius){
           double pi = 3.14;
           double circ = pi*radius*2;
           return circ;
         }      
                ]] >     
    <!-- Remove the space between ]] and > in the preceding line. -->
    <!-- The space is inserted because of a publishing constraint. -->
    
       </msxsl:script>
    
      <xsl:template match="data">  
      <circles>
    
      <xsl:for-each select="circle">
        <circle>
        <xsl:copy-of select="node()"/>
           <circumference>
              <xsl:value-of select="user:circumference(radius)"/>        
           </circumference>
        </circle>
      </xsl:for-each>
      </circles>
      </xsl:template>
    </xsl:stylesheet>
    					
  4. Create an XML file named XMLFile1.xml, and then add the following code:
    <?xml version='1.0'?>
    <data>
      <circle>
        <radius>12</radius>
      </circle>
      <circle>
        <radius>37.5</radius>
      </circle>
    </data>
    					
  5. Save the XML and XSL files in the application folder.
  6. Step through the code. Notice that the assemblies are created and loaded in the Output window of Visual Studio .NET.
  7. Open Performance Monitor. Notice that the private bytes display the increase in memory.

Properties

Article ID: 316775 - Last Review: February 28, 2014 - Revision: 4.0
APPLIES TO
  • Microsoft .NET Framework 4
  • Microsoft .NET Framework 2.0
  • Microsoft .NET Framework 1.0
Keywords: 
kbnosurvey kbarchive kbprb KB316775

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com