什么会导致java.lang.StackOverflowError
?我得到的堆栈打印输出不是很深(只有5种方法)。
答案 0 :(得分:57)
检查是否有任何方法的重复调用。主要是当有一个方法的递归调用时引起。一个简单的例子是
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 :(得分:21)
JVM的一个(可选)参数是堆栈大小。这是-Xss。我不知道默认值是什么,但如果堆栈上的东西总量超过该值,您将收到该错误。
通常,无限递归是导致这种情况的原因,但是如果你看到了这一点,你的堆栈跟踪将有超过5帧。
尝试添加一个-Xss参数(或增加一个值)以查看它是否消失。
答案 2 :(得分: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通常很容易解决。
从理论上讲,堆栈溢出也可能没有递归,但在实践中,它似乎是一个相当罕见的事件。
答案 3 :(得分:5)
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 frame
包含调用方法的参数,其本地参数以及方法的返回地址。返回地址表示执行点,程序执行将在调用方法返回后继续执行。如果没有新堆栈帧的空间,那么Java虚拟机(JVM)将抛出 StackOverflowError
。
可能耗尽Java应用程序堆栈的最常见情况是递归。在递归中,方法在执行期间调用自身。 Recursion
是最强大的通用编程技术之一,但必须谨慎使用,以避免 StackOverflowError
。
答案 4 :(得分:4)
当Java应用程序调用函数调用时,会在调用堆栈上分配堆栈帧。堆栈帧包含调用方法的参数,其本地参数以及方法的返回地址。
返回地址表示执行点,程序执行将在调用方法返回后继续执行。如果没有新堆栈帧的空间,那么 Java虚拟机(JVM) 会抛出StackOverflowError。
可能耗尽Java应用程序堆栈的最常见情况是递归。
请看看
答案 5 :(得分:2)
我用hibernate创建了一个程序,在其中我创建了两个POJO类,两个对象都是数据成员。当我在main方法中尝试将它们保存在数据库中时,我也遇到了这个错误。
这是因为两个类都相互引用,因此创建了一个导致此错误的循环。
因此,请检查您的程序中是否存在任何此类关系。
答案 6 :(得分:1)
当线程堆栈的大小继续增长直到达到最大限制时,可能会发生堆栈溢出异常。
调整堆栈大小(Xss和Xmso)选项......
我建议你看到这个链接: http://www-01.ibm.com/support/docview.wss?uid=swg21162896 StackOverflowError有很多可能的原因,您可以在链接....
中看到答案 7 :(得分:0)
在我的情况下,我有两项活动。在第二个活动中,我忘了将on super放在onCreate方法上。
super.onCreate(savedInstanceState);
答案 8 :(得分:0)
Hibernate用户解析数据时的解决方案:
发生这个错误是因为我正在使用杰克逊解析在@OneToMany
和@ManyToOne
两侧映射到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:不能是 集合,映射,数组或枚举。链接被处理为 用该注释注释的属性未序列化;和 反序列化期间,其值设置为具有 “托管”(转发)链接。
示例:
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。
答案 9 :(得分:0)
我遇到了同样的问题
角色.java
@ManyToMany(mappedBy = "roles", fetch = FetchType.LAZY,cascade = CascadeType.ALL)
Set<BusinessUnitMaster> businessUnits =new HashSet<>();
BusinessUnitMaster.java
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinTable(
name = "BusinessUnitRoles",
joinColumns = {@JoinColumn(name = "unit_id", referencedColumnName = "record_id")},
inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "record_id")}
)
private Set<Role> roles=new HashSet<>();
问题是当你创建BusinessUnitMaster和Role 你必须为双方保存对象 用于 RoleService.java
roleRepository.save(role);
对于 BusinessUnitMasterService.java
businessUnitMasterRepository.save(businessUnitMaster);
答案 10 :(得分:0)
在我的情况下,我不得不覆盖子类中的 equals 和 hashcode 类方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MyClass)) return false;
return id != null && id.equals(((MyClass) o).getId());
}
@Override
public int hashCode() {
return getClass().hashCode();
}
此处参考:vladmihalcea article
答案 11 :(得分:-1)
在我的情况下 toString 导致实体类中的异常检查您的系统日志它将帮助您解决异常