[英]Can a stack overflow happen for any other reason that recursion?
我得到一個代碼片段的堆棧溢出異常似乎無法產生stackoverflow ...它看起來像這樣:
public String WriteToFile(XmlDocument pDoc, String pPath)
{
string source = "";
string seq = "";
string sourcenet = "";
XmlNodelist sourceNode = pDoc.GetElementsByTagName(XmlUtils.Nodes.Source);
source = sourceNode.Item(0).InnerText;
XmlNodelist sqList= pDoc.GetElementsByTagName(XmlUtils.Nodes.Seq);
seq = sqList.Item(0).InnerText;
XmlNodelist sourceNets = pDoc.GetElementsByTagName(XmlUtils.Nodes.SourceNets);
sourcenet = sourceNets.Item(0).InnerText;
string fileName = Folders.GetMyFileName(source, seq, sourcenet);
string fullPath = Path.Combine(pPath, fileName);
pDoc.Save(pFullPathFile); <--- Stackoverflow is raised here
return pFullPathFile;
}
沒有遞歸調用,如果在轉到“外部代碼”之前檢查調用堆棧的深度為2(我猜測它不是外部的,而是啟動線程的框架的一部分,調試關閉)。
¿無論如何,除了遞歸調用之外還有什么異常可以上升嗎? 它總是在pDoc.Save方法調用中失敗...並且pDoc實際上並不那么大......更像是32KB的數據......
堆棧超出其最大大小時,可能會發生堆棧溢出異常。 這主要是通過......
堆棧溢出只是意味着你已經耗盡了堆棧,它不需要由遞歸引起。 當然,因為遞歸利用堆棧,它通常是堆棧溢出異常的原因,但它不需要。
話雖如此,根據您提供的信息,聽起來不應該在您提供的代碼中導致堆棧溢出。
默認情況下,C#中的線程具有1MB堆棧,但您可以創建具有較小堆棧的新線程 。 你是否自己在這個程序中創建線程,並設置堆棧大小?
另外,查看外部代碼部分(右鍵單擊Call Stack窗口中的External Code,選擇“Show external code”)。 看看是否有什么東西看起來是錯誤的,框架是否由於某種原因經過大量的方法調用來進行保存?
確實存在遞歸調用。
pDoc.Save()
在文檔上調用WriteTo(XmlWriter w)
,該文檔調用WriteContentTo(XmlWriter w)
。
然后,它在根級別的所有節點上調用WriteTo(XmlWriter w)
,它將包含一個元素節點(可能還有一些注釋,空格,處理指令,文檔聲明......)。
在該元素上,這將導致它寫入其標記('<',元素名稱,然后是任何屬性),然后調用WriteContentTo(XmlWriter w)
,在每個子元素上調用WriteTo(XmlWriter w)
,調用WriteContentTo(XmlWriter w)
,依此類推。
因此,這確實是每個元素如何在其子元素上調用相同方法的遞歸,並且在足夠小的堆棧空間上具有足夠深的文檔(在大多數應用程序上默認為1MB,但在ASP.NET上為256KB),您將擁有堆棧溢出。
對於記錄,只要以這種或那種方式燒毀堆棧空間,您也可以在沒有遞歸的情況下進行堆棧溢出。 stackalloc
是一個很好的方式來找到自己這樣做而只有幾個深度調用。
如果由於這種遞歸而遇到麻煩,那么請記住WriteTo
的實現本質上是(手動將WriteContentTo
內聯到其中):
w.WriteStartElement(this.Prefix, this.LocalName, this.NamespaceURI);
if (this.HasAttributes)
{
XmlAttributeCollection attributes = this.Attributes;
for (int i = 0; i < attributes.Count; i++)
{
attributes[i].WriteTo(w);
}
}
if (this.IsEmpty)
{
w.WriteEndElement();
}
else
{
for (XmlNode node = this.FirstChild; node != null; node = node.NextSibling)
{
node.WriteTo(w);
}
w.WriteFullEndElement();
}
將其替換為迭代版本,您不會溢出堆棧。 當然,如果你以某種方式設法把文件放到一個條件,它有一個元素,它是一個自己的祖先(XmlDocument防止這個?我不知道我的頭頂),然后它將轉向堆棧溢出到無限循環中,如果有什么更糟的話。
在某些語言/運行時,由於與調用堆棧本身無關的大內存分配, 可能會發生堆棧溢出。 “外部代碼”(我假設框架)完全有可能在這種情況下運行,或者實際上有一個你看不到的經典遞歸溢出問題,因為你不一定要調試它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.