C#XSLT转换内存不足

时间:2010-09-23 10:00:32

标签: c# xml xslt

全部,

我有以下使用XSLT转换XML文档的代码。 问题是当XML Document大约12MB时,C#内存不足。 是否有不同的方式进行转换而不消耗那么多内存?

public string Transform(XPathDocument myXPathDoc, XslCompiledTransform myXslTrans)
    {
        try
        {
            var stm = new MemoryStream();
            myXslTrans.Transform(myXPathDoc, null, stm);
            var sr = new StreamReader(stm);
            return sr.ReadToEnd();
        }
        catch (Exception e)
        {
          //Log the Exception
        }
    }

这是堆栈跟踪:

at System.String.GetStringForStringBuilder(String value, Int32 startIndex, Int32       length, Int32 capacity)
at System.Text.StringBuilder.GetNewString(String currentString, Int32 requiredLength)   
at System.Text.StringBuilder.Append(Char[] value, Int32 startIndex, Int32 charCount)
at System.IO.StreamReader.ReadToEnd()
at Transform(XPathDocument myXPathDoc, XslCompiledTransform myXslTrans)

6 个答案:

答案 0 :(得分:4)

我要做的第一件事就是找出问题所在。使整个MemoryStream业务无法播放,并将输出流式传输到文件,例如:

using (XmlReader xr = XmlReader.Create(new StreamReader("input.xml")))
using (XmlWriter xw = XmlWriter.Create(new StreamWriter("output.xml")))
{
   xslt.Transform(xr, xw);
}

如果你仍然得到一个内存不足的例外(我敢打赌你会折钱),这是一个非常公平的迹象,表明问题不在于输出的大小,而在于变换本身的某些东西,例如一种无限递归的东西:

<xsl:template match="foo">
   <bar>
      <xsl:apply-templates select="."/>
   </bar>
</xsl:template>

答案 1 :(得分:3)

MemoryStream + ReadToEnd意味着此时你需要在内存中有2个副本。您可以使用StringWriter对象作为目标(替换MemStream + Reader)将其优化为1个副本,并在完成后使用writer.ToString()。

但这最多只会让你达到24 MB,但仍然太小。还有别的事情要发生 无法说出什么,也许你的XSLT过于复杂或效率低下。


var writer = new StringWriter();
//var stm = new MemoryStream();
myXslTrans.Transform(myXPathDoc, null, writer);
//var sr = new StreamReader(stm);
//return sr.ReadToEnd();
return writer.ToString();

答案 2 :(得分:2)

你需要

stm.Position = 0

在使用StreamReader读取内容之前将内存流重置为开头。否则,您将尝试阅读流末尾的内容。

答案 3 :(得分:0)

ReadToEnd()函数将整个流加载到内存中。最好使用XmlReader以块的形式传输文档,然后针对较小的片段运行xslt。您可能还想考虑完全使用XmlReader传递文档,而不是使用不太适合流式传输数据的xslt,而不能使用大型文件的可扩展性。

答案 4 :(得分:0)

它可能相关也可能不相关,但您需要确保丢弃流和读取器对象。我还添加了Nick Jones指出的位置= 0。

public string Transform(XPathDocument myXPathDoc, XslCompiledTransform myXslTrans)
{
    try
    {
        using (var stm = new MemoryStream())
        {
             myXslTrans.Transform(myXPathDoc, null, stm);
             stm.Position = 0;
             using (var sr = new StreamReader(stm))
             {
                 return sr.ReadToEnd();
             }
        }
    }
    catch (Exception e)
    {
        //Log the Exception
    }
}

答案 5 :(得分:0)

确保您没有任何JavaScript,否则会出现已知的内存泄漏。

我的回复有效,可以避免许多错误和内存泄漏。用户投票给我,因为他不明白JavaScript可以作为扩展嵌入到XSLT中。

这是一篇旧文章,解释了如何做到这一点。 http://msdn.microsoft.com/en-us/magazine/cc302079.aspx

当通过扩展将JavaScript嵌入到XSLT文档中时,使用XslTransform类时,Web服务器上托管的.Net类已知存在内存泄漏。 JavaScript用于获取日期等内容并进行更多动态处理。这就是我向那些使用JavaScript扩展的人发出警告的原因。这是内存泄漏的最可能原因。

另一个警告是使用较新的XslCompliedTransform类时要小心。使用我的大型XSLT文档,我将处理器分配为XslTransform类的4倍和内存的两倍。