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)
...
public class Factorial {
public static int factorial(int n){
if(n == 1){
return 1;
}
else{
return n * factorial(n-1);
}
}
public static void main(String[] args){
System.out.println("Main method started");
int result = Factorial.factorial(-1);
System.out.println("Factorial ==>"+result);
System.out.println("Main method ended");
}
}
堆栈跟踪:
Main method started
Exception in thread "main" java.lang.StackOverflowError
at com.program.stackoverflow.Factorial.factorial(Factorial.java:9)
at com.program.stackoverflow.Factorial.factorial(Factorial.java:9)
at com.program.stackoverflow.Factorial.factorial(Factorial.java:9)
11条答案
按热度按时间yshpjwxd1#
为了描述这一点,首先让我们了解局部变量和对象是如何存储的。
局部变量存储在堆栈中:
如果你看了图片,你应该能够理解事情是如何运作的。
当java应用程序调用函数调用时,会在调用堆栈上分配一个堆栈帧。堆栈框架包含被调用方法的参数、其本地参数和方法的返回地址。返回地址表示执行点,在调用的方法返回后,程序将从该点继续执行。如果没有新堆栈帧的空间,则
StackOverflowError
由java虚拟机(jvm)抛出。可能耗尽java应用程序堆栈的最常见情况是递归。在递归中,方法在执行期间调用自身。递归被认为是一种强大的通用编程技术,但必须谨慎使用,以避免
StackOverflowError
.掷硬币的例子
StackOverflowError
如下所示:stackoverflowerrorexample.java文件:
在本例中,我们定义了一个递归方法,称为
recursivePrint
它打印一个整数,然后调用自身,并将下一个连续整数作为参数。递归结束,直到我们传入为止0
作为参数。但是,在我们的示例中,我们从1及其递增的跟随者传入参数,因此,递归将永远不会终止。一个示例执行,使用
-Xss1M
将线程堆栈的大小指定为1mb的标志,如下所示:根据jvm的初始配置,结果可能会有所不同,但最终
StackOverflowError
将被抛出。这个例子是一个非常好的例子,说明了如果不谨慎地实现递归,它会导致问题。如何处理堆栈溢出错误
最简单的解决方案是仔细检查堆栈跟踪并检测行号的重复模式。这些行号表示递归调用的代码。一旦检测到这些行,就必须仔细检查代码并理解为什么递归永远不会终止。
如果您已经验证了递归的实现是正确的,那么您可以增加堆栈的大小,以便允许更多的调用。根据安装的java虚拟机(jvm),默认线程堆栈大小可能等于512kb或1mb。可以使用
-Xss
旗帜。可以通过项目的配置或命令行指定此标志。文件的格式-Xss
参数是:-Xss<size>[g|G|m|M|k|K]
irlmq6kh2#
堆栈溢出的最常见原因是太深或无限递归。如果这是您的问题,那么本关于java递归的教程可以帮助您理解这个问题。
u1ehiz5o3#
堆栈溢出通常是通过嵌套太深的函数调用来调用的(特别是在使用递归时,即调用自身的函数)或在堆栈上分配大量内存(使用堆更合适)。
e5njpo684#
如果你有这样一个函数:
然后foo()会不断地调用自己,越来越深,当用来跟踪所使用函数的空间被填满时,就会出现堆栈溢出错误。
zengzsys5#
就像你说的,你需要展示一些代码。:-)
当函数调用嵌套太深时,通常会发生堆栈溢出错误。请参阅stack overflow code golf thread以获取有关如何发生这种情况的一些示例(尽管在该问题中,答案故意导致堆栈溢出)。
smdnsysy6#
一
StackOverflowError
是java中的运行时错误。当超过jvm分配的调用堆栈内存量时抛出。
一种常见的
StackOverflowError
当调用堆栈由于过度的深递归或无限递归而超出时抛出。例子:
堆栈跟踪:
在上述情况下,可以通过进行编程更改来避免这种情况。但是如果程序逻辑是正确的并且仍然发生,那么堆栈大小需要增加。
kg7wmglp7#
下面是一个用于反转单链表的递归算法的示例。在具有以下规格(4g内存、intel core i5 2.3ghz cpu、64位windows 7)的笔记本电脑上,对于大小接近10000的链表,此函数将遇到stackoverflow错误。
我的观点是,我们应该明智地使用递归,总是考虑到系统的规模。通常递归可以转换成迭代程序,这样可以更好地扩展(在页面底部给出了同一算法的一个迭代版本,它可以在9毫秒内反转大小为100万的单链表。)
相同算法的迭代版本:
w51jfk4q8#
参数和局部变量在堆栈上分配(对于引用类型,对象位于堆上,堆栈中的变量引用堆上的对象)。堆栈通常位于地址空间的上端,当它用完时,它会朝向地址空间的底部(即朝向零)。
您的进程还有一个堆,它位于进程的底部。当您分配内存时,这个堆可以向地址空间的上端增长。如您所见,堆有可能与堆“碰撞”(有点像板块构造!!!)。
堆栈溢出的常见原因是错误的递归调用。通常,这是因为递归函数没有正确的终止条件,所以它最终会永远调用自己。或者当终止条件很好时,可能是因为在实现之前需要太多的递归调用。
但是,通过gui编程,可以生成间接递归。例如,您的应用程序可能正在处理绘制消息,并且在处理这些消息时,它可能会调用导致系统发送另一条绘制消息的函数。这里您没有显式地调用自己,但是os/vm已经为您完成了。
要处理它们,需要检查代码。如果你有自己调用的函数,那么检查你是否有终止条件。如果有,那么检查调用函数时是否至少修改了一个参数,否则递归调用的函数将没有可见的更改,终止条件也没有用。还要注意,在达到有效的终止条件之前,堆栈空间可能会耗尽内存,因此请确保方法能够处理需要更多递归调用的输入值。
如果没有明显的递归函数,那么检查是否调用了任何间接导致函数被调用的库函数(如上面的隐式情况)。
o4tp2gmn9#
堆栈的空间限制取决于操作系统,正常大小是8mb(在ubuntu中,您可以使用
$ ulimit -u
也可以在其他操作系统中进行检查(类似地)。任何程序都会在运行时使用堆栈,但要完全知道何时使用堆栈,需要检查汇编语言。例如,在x86\u 64中,堆栈用于:进行过程调用时保存返回地址
保存局部变量
保存特殊寄存器以便以后恢复
向过程调用传递参数(多于6个)
其他:随机未使用的堆栈基、加那利值、填充。。。等
如果您不知道x86_64(正常情况),您只需要知道您使用的特定高级别编程语言何时编译这些操作。例如在c中:
(1) → 函数调用
(2) → 函数调用中的局部变量(包括main)
(3) → 函数调用中的局部变量(非main)
(4) → 函数调用
(5) → 通常是函数调用,它通常与堆栈溢出无关。
因此,在c中,只有局部变量和函数调用使用堆栈。使堆栈溢出的两种(独特的?)方法是:
在main或其调用的任何函数中声明过大的局部变量(
int array[10000][10000];
)非常深或无限的递归(同时调用太多函数)。
为了避免
StackOverflowError
你可以:检查局部变量是否太大(1 mb的顺序)→ 使用堆(malloc/calloc调用)或全局变量。
检查inf
gpfsuwkq10#
堆栈溢出的意思就是:堆栈溢出。通常在程序中有一个包含局部作用域变量和地址的堆栈,当例程执行结束时,这些变量和地址将返回。堆栈往往是内存中某个位置的固定内存范围,因此它可以包含多少值是有限的。
如果堆栈是空的,你不能弹出,如果你这样做,你会得到堆栈下溢错误。
如果堆栈已满,则无法推送,否则将出现堆栈溢出错误。
因此,当您在堆栈中分配太多时,就会出现堆栈溢出。例如,在前面提到的递归中。
有些实现优化了某些形式的递归。尤其是尾部递归。尾部递归例程是例程的一种形式,其中递归调用作为例程所做的最后一件事出现。这样的例行呼叫被简化为跳转。
有些实现甚至实现自己的递归堆栈,因此它们允许递归继续,直到系统内存耗尽。
如果可以的话,最简单的方法就是增加堆栈大小。如果您不能做到这一点,那么第二个最好的方法就是查看是否有明显的原因导致堆栈溢出。尝试在调用之前和之后打印一些东西到例程中。这有助于你找出失败的例行公事。
lx0bsm1f11#
StackOverflowError
在堆栈中作为OutOfMemoryError
是一堆。无限递归调用会导致堆栈空间被耗尽。
下面的示例生成
StackOverflowError
: