什么是StackOverflowError,是什么原因导致的,我应该如何处理它们?
当前回答
如果你有这样一个函数:
int foo()
{
// more stuff
foo();
}
然后foo()将继续调用自己,越调用越深,当用于跟踪所处函数的空间被填满时,就会得到堆栈溢出错误。
其他回答
堆栈溢出的意思就是:堆栈溢出。通常在程序中有一个堆栈,它包含局部作用域变量和例程执行结束时返回的地址。该堆栈往往是内存中的某个固定内存范围,因此它可以包含多少值是有限的。
如果堆栈是空的,你不能弹出,如果你这样做,你会得到堆栈溢出错误。
如果堆栈满了,你就不能推,如果你推了,你就会得到堆栈溢出错误。
当你在堆栈中分配了太多内存时,就会出现堆栈溢出。例如,在前面提到的递归中。
有些实现优化了某些形式的递归。特别是尾递归。尾递归例程是一种例程,其中递归调用作为例程所做的最后一件事出现。这样的例行调用被简化为跳转。
有些实现甚至实现了自己的递归堆栈,因此它们允许递归继续进行,直到系统内存耗尽。
你可以尝试的最简单的事情就是如果可以的话增加你的堆栈大小。如果不能这样做,那么第二种方法是查看是否有什么东西明显导致堆栈溢出。试着在调用前后打印一些东西到例程中。这可以帮助您找到失败的例程。
要描述这一点,首先让我们了解局部变量和对象是如何存储的。
局部变量存储在堆栈上:
如果你看了图片,你应该能够理解事情是如何工作的。
当Java应用程序调用函数时,将在调用堆栈上分配堆栈帧。堆栈帧包含被调用方法的参数、局部参数和方法的返回地址。返回地址表示执行点,在调用的方法返回后,程序将从该执行点继续执行。如果没有空间用于新的堆栈帧,那么Java虚拟机(JVM)将抛出StackOverflowError。
可能耗尽Java应用程序堆栈的最常见情况是递归。在递归中,方法在执行过程中调用自身。递归被认为是一种强大的通用编程技术,但必须谨慎使用,以避免StackOverflowError。
抛出StackOverflowError的示例如下所示:
StackOverflowErrorExample.java:
public class StackOverflowErrorExample {
public static void recursivePrint(int num) {
System.out.println("Number: " + num);
if (num == 0)
return;
else
recursivePrint(++num);
}
public static void main(String[] args) {
StackOverflowErrorExample.recursivePrint(1);
}
}
在本例中,我们定义了一个递归方法recursivePrint,它打印一个整数,然后调用自身,并将下一个连续整数作为参数。递归结束,直到我们传入0作为参数。然而,在我们的例子中,我们传递了参数1和它不断增加的追随者,因此,递归永远不会结束。
一个示例执行,使用-Xss1M标志,指定线程堆栈的大小为1mb,如下所示:
Number: 1
Number: 2
Number: 3
...
Number: 6262
Number: 6263
Number: 6264
Number: 6265
Number: 6266
Exception in thread "main" java.lang.StackOverflowError
at java.io.PrintStream.write(PrintStream.java:480)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)
at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
at java.io.PrintStream.write(PrintStream.java:527)
at java.io.PrintStream.print(PrintStream.java:669)
at java.io.PrintStream.println(PrintStream.java:806)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:4)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
...
根据JVM的初始配置,结果可能不同,但最终将抛出StackOverflowError。这个例子很好地说明了如果不小心实现递归,它是如何导致问题的。
如何处理StackOverflowError
The simplest solution is to carefully inspect the stack trace and detect the repeating pattern of line numbers. These line numbers indicate the code being recursively called. Once you detect these lines, you must carefully inspect your code and understand why the recursion never terminates. If you have verified that the recursion is implemented correctly, you can increase the stack’s size, in order to allow a larger number of invocations. Depending on the Java Virtual Machine (JVM) installed, the default thread stack size may equal to either 512 KB, or 1 MB. You can increase the thread stack size using the -Xss flag. This flag can be specified either via the project’s configuration, or via the command line. The format of the -Xss argument is: -Xss<size>[g|G|m|M|k|K]
StackOverflowError之于堆栈,就像OutOfMemoryError之于堆。
无界递归调用会导致堆栈空间被用完。
产生StackOverflowError的示例如下:
class StackOverflowDemo
{
public static void unboundedRecursiveCall() {
unboundedRecursiveCall();
}
public static void main(String[] args)
{
unboundedRecursiveCall();
}
}
如果对递归调用进行了限制,以防止内存中不完整调用的总和(以字节为单位)超过堆栈大小(以字节为单位),则可以避免StackOverflowError。
如您所说,您需要展示一些代码。: -)
堆栈溢出错误通常发生在函数调用nest太深的时候。请参阅Stack Overflow Code Golf线程,以了解如何发生这种情况(尽管在这个问题的情况下,答案会故意导致堆栈溢出)。
堆栈溢出通常是由于嵌套函数调用太深(在使用递归时尤其容易,即函数调用自身)或在堆栈上分配大量内存而使用堆更合适。
推荐文章
- 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对象转换为日历对象