什么会引起java.lang.StackOverflowError
?我得到的堆栈打印输出根本不是很深(只有5种方法)。
Answers:
检查对方法的任何回溯调用。主要是在递归调用方法时引起的。一个简单的例子是
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时将被反复推入堆栈。
实际上,导致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通常很容易解决。
从理论上讲,也有可能在没有递归的情况下发生堆栈溢出,但是在实践中,这似乎是相当罕见的事件。
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
出现这种情况。
当Java应用程序调用函数调用时,将在调用堆栈上分配一个堆栈框架。堆栈框架包含调用的方法的参数,其本地参数以及方法的返回地址。
返回地址表示执行点,在调用点返回后,程序将从该执行点继续执行。如果没有空间用于新的堆栈框架,那么Java虚拟机(JVM)会引发StackOverflowError。
可能耗尽Java应用程序堆栈的最常见情况是递归。
请看一看
解析数据时为Hibernate用户提供的解决方案:
我遇到此错误是因为我正在解析在两侧映射的对象列表,@OneToMany
并且@ManyToOne
使用jackson到json这导致了无限循环。
如果您处在相同的情况下,则可以使用@JsonManagedReference
和@JsonBackReference
注释解决此问题。
API中的定义:
JsonManagedReference(https://fasterxml.github.io/jackson-annotations/javadoc/2.5/com/fasterxml/jackson/annotation/JsonManagedReference.html):
注释,用于指示带注释的属性是字段之间双向链接的一部分;并且它的角色是“父”(或“前进”)链接。属性的值类型(类)必须具有单个兼容的属性,并带有JsonBackReference注释。处理链接时,可以正常处理用此注释注释的属性(正常序列化,没有反序列化的特殊处理);匹配后向引用需要特殊处理
JsonBackReference:(https://fasterxml.github.io/jackson-annotations/javadoc/2.5/com/fasterxml/jackson/annotation/JsonBackReference.html):
用于指示关联属性是字段之间双向链接的一部分的注释;并且它的角色是“子”(或“后”)链接。该属性的值类型必须是Bean:它不能是Collection,Map,Array或枚举。处理链接时,用该注释注释的属性不会被序列化;在反序列化期间,其值设置为具有“托管”(转发)链接的实例。
例:
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的方法。
就我而言,我有两项活动。在第二个活动中,我忘记了在onCreate方法上放置super。
super.onCreate(savedInstanceState);
StackOverflowError
,我也不认为它正在回答问题。我认为正确的答案应该列出获取此异常的其他方法,而不是使用过多的递归,或者说除了手动抛出该异常之外,肯定没有其他方法可以获取此异常。