是什么导致java.lang.StackOverflowError


Answers:


59

检查对方法的任何回溯调用。主要是在递归调用方法时引起的。一个简单的例子是

public static void main(String... args) {
    Main main = new Main();

    main.testMethod(1);
}

public void testMethod(int i) {
    testMethod(i);

    System.out.println(i);
}

这里是System.out.println(i); 调用testMethod时将被反复推入堆栈。


1
我想你是对的。但是,解决方案是什么?因为我们正在制定一种方法来回收它,所以我们需要它。我们不想改变方法。那么如何解决这个错误呢?
Ajay Sharma

1
否则您将陷入无限循环!
yalematta

@yalematta,任何递归方法都应具有退出条件。因此,请检查您的递归方法是否已正确实施并根据某些条件完成。
Ayaz Alifov '16

@AjaySharma我们需要设计系统以适合我们分配给JVM的可用内存边界。如果系统在出现以下错误时表现不佳,则需要检查代码库。
Thota Srinath

23

JVM的(可选)参数之一是堆栈大小。是-Xss。我不知道默认值是多少,但是如果堆栈中的所有东西的数量超过该值,就会出现该错误。

通常,无限递归是造成这种情况的原因,但是如果您看到了这一点,则堆栈跟踪将包含5帧以上。

尝试添加-Xss参数(或增加其值)以查看是否消失。


10

实际上,导致java.lang.StackOverflowError的真正原因通常是无意的递归。对我来说,通常是当我打算为覆盖的方法调用超级方法时。例如在这种情况下:

public class Vehicle {
    public void accelerate(float acceleration, float maxVelocity) {
        // set the acceleration
    }
}

public class SpaceShip extends Vehicle {
    @Override
    public void accelerate(float acceleration, float maxVelocity) {
        // update the flux capacitor and call super.accelerate
        // oops meant to call super.accelerate(acceleration, maxVelocity);
        // but accidentally wrote this instead. A StackOverflow is in our future.
        this.accelerate(acceleration, maxVelocity); 
    }
}

首先,了解我们调用函数在幕后发生的事情很有用。参数和调用方法的地址被压入堆栈(请参阅http://en.wikipedia.org/wiki/Stack_(abstract_data_type)#Runtime_memory_management),以便被调用的方法可以访问参数,以便被调用方法完成后,可以在调用后继续执行。但是,由于我们递归调用this.accelerate(acceleration,maxVelocity)(方法调用自身时,递归松散。有关更多信息,请参见http://en.wikipedia.org/wiki/Recursion_(computer_science))我们处于无限递归的情况下,我们一直在调用堆栈上堆积参数并返回地址。由于调用堆栈的大小是有限的,所以我们最终会用完空间。调用堆栈上的空间用尽称为溢出。这是因为我们正在尝试使用比我们更多的堆栈空间,并且数据实际上使堆栈溢出。在Java编程语言中,这将导致运行时异常java.lang.StackOverflow,并将立即停止程序。

上面的示例在某种程度上得到了简化(尽管我发生的事情比我想承认的要多。)同一件事可能会以更全面的方式发生,从而使查找起来更加困难。但是,通常,一旦发生,StackOverflow通常很容易解决。

从理论上讲,也有可能在没有递归的情况下发生堆栈溢出,但是在实践中,这似乎是相当罕见的事件。


8

什么是 java.lang.StackOverflowError

java.lang.StackOverflowError由于深度递归(即您的程序/脚本递归太深),抛出该错误以指示应用程序堆栈已用尽。

细节

StackOverflowError扩展VirtualMachineError类这表明JVM已经或有资源的耗尽而无法进一步操作。将VirtualMachineError它扩展了Error类用于指示那些严重的问题,应用程序不应该捕获。方法可能不会在其throw子句中声明此类错误,因为这些错误是异常情况,从未发生过。

一个例子

Minimal, Complete, and Verifiable Example

package demo;

public class StackOverflowErrorExample {

    public static void main(String[] args) 
    {
        StackOverflowErrorExample.recursivePrint(1);
    }

    public static void recursivePrint(int num) {
        System.out.println("Number: " + num);

        if(num == 0)
            return;
        else
            recursivePrint(++num);
    }

}

控制台输出

Number: 1
Number: 2
.
.
.
Number: 8645
Number: 8646
Number: 8647Exception in thread "main" java.lang.StackOverflowError
    at java.io.FileOutputStream.write(Unknown Source)
    at java.io.BufferedOutputStream.flushBuffer(Unknown Source)
    at java.io.BufferedOutputStream.flush(Unknown Source)
    at java.io.PrintStream.write(Unknown Source)
    at sun.nio.cs.StreamEncoder.writeBytes(Unknown Source)
    at sun.nio.cs.StreamEncoder.implFlushBuffer(Unknown Source)
    at sun.nio.cs.StreamEncoder.flushBuffer(Unknown Source)
    at java.io.OutputStreamWriter.flushBuffer(Unknown Source)
    at java.io.PrintStream.newLine(Unknown Source)
    at java.io.PrintStream.println(Unknown Source)
    at demo.StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:11)
    at demo.StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:16)
    .
    .
    .
    at demo.StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:16)

讲解

当Java应用程序调用函数调用时,堆栈框架将在调用stack上分配。该stack frame包含调用的方法,它的本地参数和方法的返回地址的参数。返回地址表示执行点,从该执行点开始,在调用的方法返回后,程序将继续执行。如果没有空间容纳新的堆栈框架,则StackOverflowError则Java虚拟机(JVM)会抛出该异常。

可能耗尽Java应用程序堆栈的最常见情况是递归。作为递归,方法在执行期间会自行调用。Recursion一种最强大的通用编程技术,但必须谨慎使用,以免StackOverflowError出现这种情况。

参考文献


4

当Java应用程序调用函数调用时,将在调用堆栈上分配一个堆栈框架。堆栈框架包含调用的方法的参数,其本地参数以及方法的返回地址。

返回地址表示执行点,在调用点返回后,程序将从该执行点继续执行。如果没有空间用于新的堆栈框架,那么Java虚拟机(JVM)会引发StackOverflowError

可能耗尽Java应用程序堆栈的最常见情况是递归。

请看一看

如何解决StackOverflowError


2

我使用hibernate创建了一个程序,在其中创建了两个POJO类,两个POJO类彼此之间都作为数据成员。当在主要方法中尝试将它们保存在数据库中时,我也遇到此错误。

发生这种情况是因为两个类都互相引用,因此创建了一个导致此错误的循环。

因此,请检查程序中是否存在任何此类关系。


2

解析数据时为Hibernate用户提供的解决方案:

我遇到此错误是因为我正在解析在两侧映射的对象列表,@OneToMany并且@ManyToOne使用jackson到json这导致了无限循环。

如果您处在相同的情况下,则可以使用@JsonManagedReference@JsonBackReference注释解决此问题。

API中的定义:

例:

Owner.java:

@JsonManagedReference
@OneToMany(mappedBy = "owner", fetch = FetchType.EAGER)
Set<Car> cars;

Car.java:

@JsonBackReference
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "owner_id")
private Owner owner;

另一种解决方案是使用@JsonIgnore它将字段设置为null的方法。


1

当线程堆栈的大小继续增长直至达到最大限制时,可能会发生堆栈溢出异常。

调整堆栈大小(Xss和Xmso)选项...

我建议您看到此链接:http : //www-01.ibm.com/support/docview.wss?uid=swg21162896有很多可能的原因导致StackOverflowError,如您在链接中所看到的。


通常,仅链接答案是不可接受的;链接中断将完全使答案无效。请提供一些上下文,代码和答案说明,而不仅仅是链接。
杰伊(Jay)

0

就我而言,我有两项活动。在第二个活动中,我忘记了在onCreate方法上放置super。

super.onCreate(savedInstanceState);

即使有可能提出StackOverflowError,我也不认为它正在回答问题。我认为正确的答案应该列出获取此异常的其他方法,而不是使用过多的递归,或者说除了手动抛出该异常之外,肯定没有其他方法可以获取此异常。
JojOatXGME
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.