2010-09-23 27 views
2

全部,C#XSLT轉換內存不足

我使用XSLT轉換XML文檔的代碼如下。 問題是當XML文檔大約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) 
+2

您能否提供完整的異常詳細信息,即catch塊中的e.ToString()的輸出?你還可以展示你的變換和(縮小的)樣本輸入文檔嗎? – 2010-09-23 10:20:59

+0

還有其他的東西:當它與10MB輸入一起工作時,產生的字符串有多大? – 2010-09-23 11:05:46

+0

它可能是你有一個xslt導致大量輸出的問題,你有沒有嘗試運行在Visual Studio中的xml或其他工具之外的代碼提供的上下文中的轉換? – TheCodeKing 2010-09-23 14:56:10

回答

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(); 
+1

我假設這個異常已經發生在早期,即在'myXslTrans.Transform'中。但沒有堆棧跟蹤,我們只能猜測。 – 2010-09-23 10:20:16

+0

在原帖中添加了堆棧跟蹤 – koumides 2010-09-23 10:26:22

+0

您能否提供一個如何更換它的例子? – koumides 2010-09-23 10:48:26

0

的ReadToEnd的()函數加載整個流到內存中。您最好使用XmlReader以塊形式流式傳輸文檔,然後針對較小的碎片運行xslt。您可能還想考慮將文檔完全傳遞給XmlReader,而不是使用xslt,它不適合流式傳輸數據,對於大型文件可擴展性較差。

2

你需要

stm.Position = 0 

通過StreamReader讀取內容之前,內存流的開頭重置。否則,您正嘗試從流末尾讀取內容。

+0

我確實有過這個,但沒有任何區別 – koumides 2010-09-23 10:34:23

0

它可能相關,也可能不相關,但您需要確保處置流和讀取器對象。尼克瓊斯指出,我還補充了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 
    } 
} 
+0

雖然這是一個好習慣,但MemoryStream實際上並不需要Disposing。 – 2010-09-23 10:59:10

+0

是的,沒有。據我瞭解,如果任何異步方法已被調用(BeginRead,BeginWrite),並沒有完成,你可能會泄漏事件句柄,儘管不太可能。正如你所說,這是一個好習慣。 – Bronumski 2010-09-23 11:57:19

+1

@Henk實現IDisposable的目的是讓調用者知道總是儘可能地釋放對象來釋放資源。國際海事組織我不認爲有沒有這樣做的理由,或沒有理由不這樣做。如果你看看MemoryStream.Dispose在反射器中的實現,那麼這樣做會有一些後果,儘管很小。我會一直考慮不把一次性使用的物體作爲一個bug。 – TheCodeKing 2010-09-23 14:50:17

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> 
0

確保您沒有任何JavaScript,否則存在已知的內存泄漏。

我的回答有效,可以避免很多錯誤和內存泄漏。一位用戶投我不喜歡,因爲他不明白JavaScript可以作爲擴展插入到XSLT中。

這是一篇介紹如何去做的舊文章。 http://msdn.microsoft.com/en-us/magazine/cc302079.aspx

當JavaScript通過擴展插入到XSLT文檔中時,在Web服務器上託管的.Net類在使用XslTransform類時存在已知的內存泄漏。JavaScript被用來獲取諸如日期之類的東西並做一些更動態的處理。這就是爲什麼我給那些使用JavaScript擴展的人發出警告。這是內存泄漏的最可能原因。

另一個警告是要小心使用較新的XslCompliedTransform類。使用我的大型XSLT文檔,我將處理器分配到XslTransform類的4倍和兩倍的內存。