关于Java的StackOverflow输出的问题

时间:2016-10-19 22:26:53

标签: java exception recursion

当java抛出溢出异常并且你开始在输出中看到红色时,程序在什么时候停止输出/哪里是相对于导致它的行的执行引发的错误消息?

例如,我正在运行一个递归程序,为了调试目的,我每次递归都会打印它的值。在程序停止递归/打印之前很久就会出现堆栈溢出错误,但是当我按照输出到底部时,程序正在按照我想要的方式执行,直到它停止打印为止。当java检测到溢出然后程序继续迭代时,是否打印错误,或者是从我的程序输出堆栈溢出的最后一行?

我知道这可能措辞不好,我相当新手,而且可能有更有效的方式来表达我的问题。

1 个答案:

答案 0 :(得分:1)

为了防止无限递归循环,JVM有一个内置的检查,看看是否有太多嵌套的堆栈帧(一个主要与函数调用相关的构造)。

当抛出异常时,当它从原始堆栈帧移动到更高的堆栈帧时,JVM会向异常添加一个注释(这在幕后自动完成),以详细说明它将离开的帧,以及如果行号表可用,则该行号与引发异常的操作相关联。

有时会捕获异常,在这种情况下,有关它所经历的帧的信息可能永远不会被使用(谁知道,也许到现在它甚至都不会被生成,JVM充满惊人和奇妙的优化) ,但是如果它达到顶层,它就会打印出来,打印例程知道如何查看异常存储的行进路径,并按照我们现在的预期将它们打印出来。

由于递归例程通常比允许的堆栈帧堆栈更深入,因此您需要重写递归以不依赖于堆栈帧。它只是不支持您需要的深度。为此,您通常会获取将存储在堆栈帧的各个级别中的数据,并将它们放在“类似对象的帧”中。由于它现在位于对象中,而不在帧中,因此您不再局限于帧深度,而是仅限于可用堆内存。

这也意味着您需要重写算法以从堆存储堆栈开始。然后它需要以递归方法所具有的方式推送和弹出正确的上下文。

总而言之,这不是一件非常困难的事情,但是如果你是第一次这样做的话,它似乎就不熟悉了。

借用another question并稍微压缩它,你会转换像

这样的算法
algorithm search(NODE):
  doSomethingWith(NODE)
  for each node CHILD connected to NODE:
    search(CHILD)

algorithm search(NODE):
  createStack()
  addNodeToStack(NODE)

  while(stackHasElements)
      NODE = popNodeFromStack()
      doSomethingWith(NODE)
      for each node CHILD connected to NODE:
         addNodeToStack(CHILD)

通过将递归推送到堆上,这将有效地允许您使用相同的堆栈帧来执行整个递归调用。