我到处找都找不到确切的答案。根据文档,Java在以下情况下抛出Java .lang. stackoverflowerror错误:

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

但这引发了两个问题:

除了通过递归,没有其他方法可以导致堆栈溢出吗? StackOverflowError发生在JVM溢出堆栈之前还是之后?

关于第二个问题:

当Java抛出StackOverflowError时,您是否可以安全地假设堆栈没有写入堆?如果您在抛出堆栈溢出的函数的try/catch中缩小堆栈或堆的大小,您可以继续工作吗?这在任何地方都有记录吗?

我不想要的答案:

发生StackOverflow是由于糟糕的递归。 当堆遇到堆栈时发生StackOverflow。


当前回答

没有“StackOverFlowException”。你的意思是“StackOverFlowError”。

是的,如果你抓住它,你可以继续工作,因为当你这样做的时候,堆栈被清除了,但这将是一个糟糕和丑陋的选择。

什么时候抛出错误?—当您调用一个方法时,JVM会验证是否有足够的内存来执行该操作。当然,如果不可能,则抛出错误。

不,这是得到错误的唯一方法:使堆栈满。但不仅通过递归,还调用无限调用其他方法的方法。这是一个非常特殊的错误,所以不是。 它在堆栈满之前抛出,恰好在您验证它的时候抛出。如果没有可用空间,您将把数据放在哪里?凌驾他人之上?没有。

其他回答

似乎您认为stackoverflow错误类似于本机程序中的缓冲区溢出异常,此时存在将未分配给缓冲区的内容写入内存的风险,从而破坏一些其他内存位置。根本不是这样的。

JVM为每个线程的每个堆栈分配了一个给定的内存,如果试图调用一个方法恰好填满了这个内存,JVM就会抛出一个错误。就像你想写一个长度为N的数组的下标N一样,不会发生内存损坏。堆栈不能写入堆。

StackOverflowError之于堆栈,就像OutOfMemoryError之于堆:它只是表明没有更多可用内存。

来自虚拟机错误的描述(§6.3)

StackOverflowError: Java虚拟机实现已经耗尽了线程的堆栈空间,通常是因为由于执行程序中的错误,线程正在执行无限数量的递归调用。

StackOverflow发生在函数调用时,堆栈已满。

就像一个ArrayOutOfBoundException。它不会腐蚀任何东西,事实上很有可能抓住它并从中恢复。

它通常是由于不受控制的递归导致的,但也可能是由于调用了非常深的函数堆栈而导致的。

StackOverFlowError最常见的原因是过度深度递归或无限递归。

例如:

public int yourMethod(){
       yourMethod();//infinite recursion
}

在Java中:

内存中有两个区域:堆和堆栈。堆栈内存用于存储局部变量和函数调用,而堆内存用于存储Java中的对象。

如果堆栈中没有剩余内存用于存储函数调用或本地变量,JVM将抛出java.lang.StackOverFlowError

而如果没有更多的堆空间来创建对象,JVM将抛出java.lang.OutOfMemoryError

在c#中,可以通过错误地定义对象属性来实现堆栈溢出。 例如:

private double hours;

public double Hours
        {
            get { return Hours; }
            set { Hours = value; }
        }

如你所见,它会一直返回带有大写H的Hours, H本身也会返回Hours,等等。

堆栈溢出也经常发生,因为内存不足,或者当使用托管语言时,因为您的语言管理器(CLR, JRE)将检测到您的代码陷入无限循环。

除了通过递归,没有其他方法可以导致堆栈溢出吗?

当然。只需要继续调用方法,而不返回。不过,您将需要很多方法,除非您允许递归。实际上,这并没有什么区别:一个堆栈框架就是一个堆栈框架,不管它是不是递归方法之一都是一样的。

第二个问题的答案是:当JVM试图为下一个调用分配堆栈帧时,发现不可能分配堆栈帧,就会检测到stackoverflow。所以,没有东西会被覆盖。