簡體   English   中英

由於遞歸的任何其他原因,堆棧溢出是否會發生?

[英]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的數據......

堆棧超出其最大大小時,可能會發生堆棧溢出異常。 這主要是通過......

  • 有一個深度嵌套的堆棧,不是遞歸的。 想想事件風暴,其中事件A導致事件B導致事件C,所有事件都具有深度增長堆棧的處理程序。
  • 在一些大的堆棧分配之后發生淺堆棧

堆棧溢出只是意味着你已經耗盡了堆棧,它不需要由遞歸引起。 當然,因為遞歸利用堆棧,它通常是堆棧溢出異常的原因,但它不需要。

話雖如此,根據您提供的信息,聽起來不應該在您提供的代碼中導致堆棧溢出。

默認情況下,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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM