【问题标题】:Questions about Java's StackOverflow outputs关于 Java 的 StackOverflow 输出的问题
【发布时间】:2017-03-01 18:01:20
【问题描述】:

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

例如,我正在运行一个递归程序,出于调试目的,我让它在每次递归时打印它的值。在程序停止递归/打印之前很久就会弹出一个 stackoverflow 错误,但是当我按照输出到底部时,程序正在执行我想要的操作,直到它停止打印。是java检测到溢出然后程序继续迭代时打印的错误,还是我的程序输出的最后一行发生堆栈溢出?

我知道这可能措辞不佳,我是新手,可能有一种更有效的方式来表达我的问题。

【问题讨论】:

  • 您在堆栈跟踪中看到的异常应该大致在它发生的那一刻打印出来。
  • 您是在询问在检测到 StackOverflow 之前堆栈跟踪需要多少次函数调用?
  • 我认为在构建堆栈帧时会发生溢出。也就是说,在尝试进行方法调用期间,在该方法内的任何代码开始执行之前。
  • 另外,很明显,一个代码 sn-p 和异常的前 20 行左右可以帮助我们更具体。

标签: java exception recursion


【解决方案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)

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

【讨论】:

    猜你喜欢
    • 2010-09-28
    • 2011-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-20
    • 1970-01-01
    • 2019-02-26
    • 2011-03-27
    相关资源
    最近更新 更多