***Error 啥时候发生? [复制]

Posted

技术标签:

【中文标题】***Error 啥时候发生? [复制]【英文标题】:When does ***Error occur? [duplicate]***Error 什么时候发生? [复制] 【发布时间】:2014-01-06 15:19:14 【问题描述】:

根据 Oracle 的说法,***Error 是:

由于应用程序递归太深而发生堆栈溢出时引发。

我知道什么是递归,通常递归函数,如果没有正确终止,会导致 ***Error。为了检查在***Error 被抛出之前发生的递归调用的数量,我编写了以下代码:

package ErrorCases;

public class ***Error 
static int i=0;
void a()


    //System.out.println("called "+(++i));
    try
        ++i;
    a();
    catch(Error e)
    
        System.out.println(e.getClass());
        System.out.println(i);
    


public static void main(String[] args) 

       new ***Error().a();

   


i 的值给出了在 JVM 抛出 ***Error 之前对 a() 的递归调用计数。i 的值在每次运行中都不同,例如:

output 1: class java.lang.***Error
           10466
Output 2: class java.lang.***Error
           10470

我的查询是?

    在 JVM 抛出之前,递归必须发生多深 ***Error?

    一旦***Error 有 被扔了?

【问题讨论】:

从堆栈溢出中恢复需要完全禁欲——哎呀,我的意思是非常不确定。如果代码当时正在进行一些复杂的操作,则可能没有足够的堆栈空间允许它退出。并且许多正在进行的操作可能无法防止此类故障,并且根本无法退出/重置。 我读了很长时间的最佳问题。让我思考!谢谢 【参考方案1】:

深度取决于两件事:

1:栈的大小。

2:每次递归使用的堆栈空间量。

函数参数、局部变量和返回地址都在栈上分配,对象在堆上分配。

恢复

有可能恢复。

try 
    myDeepRecursion();
 catch (***Error e) 
  // We are back from deep recursion. Stack should be ok again.

但是,请注意以下有关错误的信息(来自 java API 文档):

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch.

编辑:

注意事项:虽然在递归函数中捕捉异常是可以的,但不要试图捕捉错误。如果堆栈已满,则错误处理将导致新的错误。简单的事情,例如对System.out.println() 的调用将失败,因为堆栈上没有空间用于返回地址。

这就是为什么错误应该在递归函数之外被捕获。

【讨论】:

我们可以控制深度吗? 你可以使用 jvm 选项 -Xss 解释here ***.com/questions/3700459/… 不是来自外部。 JVM 没有“maxRecursionDepth”参数。但是,您可以使用 -Xss JVM 参数指定堆栈大小。 在我的回答中添加了一个编辑。【参考方案2】:

在 JVM 抛出 ***Error 之前,递归必须发生多深?

这实际上取决于您正在使用的机器以及您的 JVM 及其配置。 (在我当前的设置中是 6000 - 8500,在后台有很多任务),最多是你在应用程序和递归方法中执行的操作。

一旦 ***Error 被抛出,我们可以恢复吗?

不!我Java抛出一个错误,没有办法恢复正常。这是异常和错误之间的主要区别。

错误是 Throwable 的子类,表示合理的应用程序不应尝试捕获的严重问题

Read more about Errors here

Read more about Java stack size myths here

编辑:

发生错误后你仍然可以做事,但这有意义吗?您的代码中有严重错误!您无法确定一切正常!

您的代码没有终止,因为您再次调用该方法,这就像一个无限循环一样

【讨论】:

他们为什么每次运行时输出显示不同的值?我在同一台机器上运行 基本上是的。计算机是一台工作机器,有或多或少的事情要做 catch(Error e), 调用其他函数,如果栈满了,它是怎么工作的? 请检查编辑。 请检查第二个代码。它没有终止。它是如何打印堆栈的?【参考方案3】:

您可能会看到不同的堆栈深度的原因是堆栈帧的大小不一定相同。例如,Hotspot JVM 对 JIT 编译代码和解释代码有不同的堆栈帧。 JIT 编译器与正在运行的代码并行工作,因此机器上的负载等外部因素可能会影响 JVM 何时/是否开始使用 JIT 堆栈帧。

您应该记住的一件事是,当您获得***Error 时,几乎您所做的任何事情都可能导致另一个***Error。例如在catch 部分打印错误类的名称也可能会用完堆栈空间,因此另一个***Error 将被扔下堆栈。

有些人声称您无法从Error 中恢复,因为 JVM 正在关闭。这是不正确的;您可以从一些错误中恢复。 ***ErrorOutOfMemoryError 的棘手之处在于,一旦你抓住了一个,你就绝对不能保证程序的状态。例如,您使用的关键数据结构可能处于不一致的状态。这通常会使恢复变得困难或不可能。

【讨论】:

感谢您的回答,如果***Error 表示“没有剩余堆栈”?为什么我可以从catch() 内部调用函数b() 抛出错误时,从堆栈中移除最顶层的帧,并由调用方法中的catch 块处理错误。如果没有足够的堆栈空间,则会抛出另一个 ***Error 并删除另一个框架。这个过程一直持续到删除了这么多帧,以至于 catch 块可以完成而不会引发错误。【参考方案4】:

参数和局部变量在堆栈上分配(对象存在于堆上的引用类型和引用该对象的变量)。堆栈通常位于地址空间的上端,当它用完时,它会朝向地址空间的底部(即朝向零)。

您的进程也有一个堆,它位于进程的底部。当你分配内存时,这个堆会增长到地址空间的上端。如您所见,堆有可能与堆栈“碰撞”(有点像技术板!!!)。 [source]

因此 StackOIverflowError 将取决于您的堆栈大小以及堆大小,由于 GC 等许多因素,堆栈大小将取决于执行与执行。

正如名字所说,它是错误而不是异常。没有任何恢复。 JVM 将关闭。

【讨论】:

谢谢,但是如果JVM关闭了,那我们怎么catch它运行代码呢? 只有在没有捕捉到错误的情况下,JVM才会关闭。 Exception也一样,那么,从这个意义上讲,Exception如果不处理,会导致JVM崩溃。 但是捕获的异常应该被处理,未捕获的异常在某些情况下根本没有错误。 如果你在 Throwable 一直“冒泡”之前捕获它,它不会关闭 JVM。如果你没有抓住它,它会的。这适用于所有 Throwable,无论是异常还是错误。不同之处在于一个人应该对它们做什么,这总是需要谨慎对待。

以上是关于***Error 啥时候发生? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

Objective-C 中的“&error”是啥意思? [复制]

函数返回时堆栈会发生啥? [复制]

notifyAll() 会发生啥? [复制]

循环中的 Lambda 变量捕获 - 这里会发生啥? [复制]

当 Promise 永远不会解决时会发生啥? [复制]

C - free() 之后内存会发生啥? [复制]