什么是StackOverflowError,是什么原因导致的,我应该如何处理它们?
当前回答
堆栈溢出最常见的原因是过于深入或无限递归。如果这是你的问题,这个关于Java递归的教程可以帮助你理解这个问题。
其他回答
堆栈溢出的意思就是:堆栈溢出。通常在程序中有一个堆栈,它包含局部作用域变量和例程执行结束时返回的地址。该堆栈往往是内存中的某个固定内存范围,因此它可以包含多少值是有限的。
如果堆栈是空的,你不能弹出,如果你这样做,你会得到堆栈溢出错误。
如果堆栈满了,你就不能推,如果你推了,你就会得到堆栈溢出错误。
当你在堆栈中分配了太多内存时,就会出现堆栈溢出。例如,在前面提到的递归中。
有些实现优化了某些形式的递归。特别是尾递归。尾递归例程是一种例程,其中递归调用作为例程所做的最后一件事出现。这样的例行调用被简化为跳转。
有些实现甚至实现了自己的递归堆栈,因此它们允许递归继续进行,直到系统内存耗尽。
你可以尝试的最简单的事情就是如果可以的话增加你的堆栈大小。如果不能这样做,那么第二种方法是查看是否有什么东西明显导致堆栈溢出。试着在调用前后打印一些东西到例程中。这可以帮助您找到失败的例程。
这是一个典型的java.lang.StackOverflowError…该方法递归地调用自身,在doubleValue()、floatValue()等中没有出口。
文件Rational.java
public class Rational extends Number implements Comparable<Rational> {
private int num;
private int denom;
public Rational(int num, int denom) {
this.num = num;
this.denom = denom;
}
public int compareTo(Rational r) {
if ((num / denom) - (r.num / r.denom) > 0) {
return +1;
} else if ((num / denom) - (r.num / r.denom) < 0) {
return -1;
}
return 0;
}
public Rational add(Rational r) {
return new Rational(num + r.num, denom + r.denom);
}
public Rational sub(Rational r) {
return new Rational(num - r.num, denom - r.denom);
}
public Rational mul(Rational r) {
return new Rational(num * r.num, denom * r.denom);
}
public Rational div(Rational r) {
return new Rational(num * r.denom, denom * r.num);
}
public int gcd(Rational r) {
int i = 1;
while (i != 0) {
i = denom % r.denom;
denom = r.denom;
r.denom = i;
}
return denom;
}
public String toString() {
String a = num + "/" + denom;
return a;
}
public double doubleValue() {
return (double) doubleValue();
}
public float floatValue() {
return (float) floatValue();
}
public int intValue() {
return (int) intValue();
}
public long longValue() {
return (long) longValue();
}
}
文件Main.java
public class Main {
public static void main(String[] args) {
Rational a = new Rational(2, 4);
Rational b = new Rational(2, 6);
System.out.println(a + " + " + b + " = " + a.add(b));
System.out.println(a + " - " + b + " = " + a.sub(b));
System.out.println(a + " * " + b + " = " + a.mul(b));
System.out.println(a + " / " + b + " = " + a.div(b));
Rational[] arr = {new Rational(7, 1), new Rational(6, 1),
new Rational(5, 1), new Rational(4, 1),
new Rational(3, 1), new Rational(2, 1),
new Rational(1, 1), new Rational(1, 2),
new Rational(1, 3), new Rational(1, 4),
new Rational(1, 5), new Rational(1, 6),
new Rational(1, 7), new Rational(1, 8),
new Rational(1, 9), new Rational(0, 1)};
selectSort(arr);
for (int i = 0; i < arr.length - 1; ++i) {
if (arr[i].compareTo(arr[i + 1]) > 0) {
System.exit(1);
}
}
Number n = new Rational(3, 2);
System.out.println(n.doubleValue());
System.out.println(n.floatValue());
System.out.println(n.intValue());
System.out.println(n.longValue());
}
public static <T extends Comparable<? super T>> void selectSort(T[] array) {
T temp;
int mini;
for (int i = 0; i < array.length - 1; ++i) {
mini = i;
for (int j = i + 1; j < array.length; ++j) {
if (array[j].compareTo(array[mini]) < 0) {
mini = j;
}
}
if (i != mini) {
temp = array[i];
array[i] = array[mini];
array[mini] = temp;
}
}
}
}
结果
2/4 + 2/6 = 4/10
Exception in thread "main" java.lang.StackOverflowError
2/4 - 2/6 = 0/-2
at com.xetrasu.Rational.doubleValue(Rational.java:64)
2/4 * 2/6 = 4/24
at com.xetrasu.Rational.doubleValue(Rational.java:64)
2/4 / 2/6 = 12/8
at com.xetrasu.Rational.doubleValue(Rational.java:64)
at com.xetrasu.Rational.doubleValue(Rational.java:64)
at com.xetrasu.Rational.doubleValue(Rational.java:64)
at com.xetrasu.Rational.doubleValue(Rational.java:64)
at com.xetrasu.Rational.doubleValue(Rational.java:64)
下面是OpenJDK 7中StackOverflowError的源代码。
StackOverflowError之于堆栈,就像OutOfMemoryError之于堆。
无界递归调用会导致堆栈空间被用完。
产生StackOverflowError的示例如下:
class StackOverflowDemo
{
public static void unboundedRecursiveCall() {
unboundedRecursiveCall();
}
public static void main(String[] args)
{
unboundedRecursiveCall();
}
}
如果对递归调用进行了限制,以防止内存中不完整调用的总和(以字节为单位)超过堆栈大小(以字节为单位),则可以避免StackOverflowError。
如您所说,您需要展示一些代码。: -)
堆栈溢出错误通常发生在函数调用nest太深的时候。请参阅Stack Overflow Code Golf线程,以了解如何发生这种情况(尽管在这个问题的情况下,答案会故意导致堆栈溢出)。
参数和局部变量分配在堆栈上(对于引用类型,对象位于堆上,堆栈中的变量引用堆上的对象)。堆栈通常位于地址空间的上端,当它被用完时,它会朝向地址空间的底部(即朝向零)。
你的进程也有一个堆,它位于你的进程的底部。当您分配内存时,这个堆可以向地址空间的顶端增长。正如您所看到的,堆有可能与堆栈“碰撞”(有点像构造板块!!)。
导致堆栈溢出的常见原因是错误的递归调用。通常,这是当递归函数没有正确的终止条件时引起的,因此它最终永远调用自己。或者当终止条件良好时,可能是由于在实现终止条件之前需要太多的递归调用而导致的。
但是,使用GUI编程,可以生成间接递归。例如,你的应用程序可能正在处理paint消息,在处理它们的同时,它可能会调用一个函数,导致系统发送另一个paint消息。这里您没有显式地调用自己,但是OS/VM已经为您完成了。
To deal with them, you'll need to examine your code. If you've got functions that call themselves then check that you've got a terminating condition. If you have, then check that when calling the function you have at least modified one of the arguments, otherwise there'll be no visible change for the recursively called function and the terminating condition is useless. Also mind that your stack space can run out of memory before reaching a valid terminating condition, thus make sure your method can handle input values requiring more recursive calls.
如果没有明显的递归函数,则检查是否调用了间接导致函数被调用的库函数(如上面的隐式情况)。
推荐文章
- javax.transaction.Transactional vs . org.springframework.transaction.annotation.Transactional
- Java 8接口方法中不允许“同步”的原因是什么?
- 如何找到Java堆大小和内存使用(Linux)?
- 使用Enum实现单例(Java)
- RabbitMQ与通道和连接之间的关系
- buildSessionFactory()配置方法在Hibernate中已弃用?
- Spring MVC -如何获得所有的请求参数在一个地图在Spring控制器?
- 如何在Java中按两个字段排序?
- 文件之间的差异。路径中的分隔符和斜杠
- 为什么不使用异常作为常规的控制流呢?
- 在方法参数中使用NotNull注释
- Spring MVC中处理可选参数的@RequestParam
- Tomcat:如何查找正在运行的Tomcat版本?
- “java”、“javaw”和“javaws”之间有什么区别?
- 将Date对象转换为日历对象