由于递归的任何其他原因,堆栈溢出是否会发生?

时间:2012-01-23 16:47:47

标签: c# exception stack-overflow

我的代码段似乎没有产生堆栈溢出的堆栈溢出异常......看起来像这样:

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的数据......

4 个答案:

答案 0 :(得分:7)

如果堆栈超出其最大大小,则可能发生堆栈溢出异常。这主要是通过......来完成的。

  • 拥有一个非递归的深层嵌套堆栈。想想事件风暴,其中事件A导致事件B导致事件C,所有事件都具有深度增长堆栈的处理程序。
  • 在一些大堆栈分配之后出现浅堆栈

答案 1 :(得分:4)

堆栈溢出只是意味着你已经耗尽了堆栈,它不需要由递归引起。当然,因为递归利用堆栈,它通常是堆栈溢出异常的原因,但它不需要。

话虽如此,根据您提供的信息,听起来并不会导致您提供的代码中出现堆栈溢出。

默认情况下,C#中的线程有1MB堆栈,但您可以create a new thread with a smaller stack。你是否自己在这个程序中创建线程,并设置堆栈大小?

另外,查看外部代码部分(右键单击Call Stack窗口中的External Code,选择“Show external code”)。看看是否有什么东西看起来是错误的,框架是否由于某种原因经过大量的方法调用来进行保存?

答案 2 :(得分:2)

确实有一个递归的电话。

pDoc.Save()在文档上调用WriteTo(XmlWriter w),调用WriteContentTo(XmlWriter w)

然后在根级别的所有节点上调用WriteTo(XmlWriter w),这将包含一个元素节点(可能还有一些注释,空格,处理指令,文档声明......)。

在该元素上,这将导致它写入其标记('&lt;',元素名称,然后是任何属性),然后调用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防止这个?我不知道我的头顶),然后它将转向堆栈溢出到无限循环中,如果有什么更糟的话。

答案 3 :(得分:0)

在某些语言/运行时,由于与调用堆栈本身无关的大内存分配,可能会发生堆栈溢出 。 “外部代码”(我假设框架)完全有可能在这种情况下运行,或者实际上有一个你看不到的经典递归溢出问题,因为你不一定要调试它。