什么是StackOverflowError,是什么原因导致的,我应该如何处理它们?
当前回答
参数和局部变量分配在堆栈上(对于引用类型,对象位于堆上,堆栈中的变量引用堆上的对象)。堆栈通常位于地址空间的上端,当它被用完时,它会朝向地址空间的底部(即朝向零)。
你的进程也有一个堆,它位于你的进程的底部。当您分配内存时,这个堆可以向地址空间的顶端增长。正如您所看到的,堆有可能与堆栈“碰撞”(有点像构造板块!!)。
导致堆栈溢出的常见原因是错误的递归调用。通常,这是当递归函数没有正确的终止条件时引起的,因此它最终永远调用自己。或者当终止条件良好时,可能是由于在实现终止条件之前需要太多的递归调用而导致的。
但是,使用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.
如果没有明显的递归函数,则检查是否调用了间接导致函数被调用的库函数(如上面的隐式情况)。
其他回答
参数和局部变量分配在堆栈上(对于引用类型,对象位于堆上,堆栈中的变量引用堆上的对象)。堆栈通常位于地址空间的上端,当它被用完时,它会朝向地址空间的底部(即朝向零)。
你的进程也有一个堆,它位于你的进程的底部。当您分配内存时,这个堆可以向地址空间的顶端增长。正如您所看到的,堆有可能与堆栈“碰撞”(有点像构造板块!!)。
导致堆栈溢出的常见原因是错误的递归调用。通常,这是当递归函数没有正确的终止条件时引起的,因此它最终永远调用自己。或者当终止条件良好时,可能是由于在实现终止条件之前需要太多的递归调用而导致的。
但是,使用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.
如果没有明显的递归函数,则检查是否调用了间接导致函数被调用的库函数(如上面的隐式情况)。
堆栈溢出通常是由于嵌套函数调用太深(在使用递归时尤其容易,即函数调用自身)或在堆栈上分配大量内存而使用堆更合适。
一个简单的Java示例,由于错误的递归调用导致Java .lang. stackoverflowerror:
class Human {
Human(){
new Animal();
}
}
class Animal extends Human {
Animal(){
super();
}
}
public class Test01 {
public static void main(String[] args) {
new Animal();
}
}
堆栈溢出的意思就是:堆栈溢出。通常在程序中有一个堆栈,它包含局部作用域变量和例程执行结束时返回的地址。该堆栈往往是内存中的某个固定内存范围,因此它可以包含多少值是有限的。
如果堆栈是空的,你不能弹出,如果你这样做,你会得到堆栈溢出错误。
如果堆栈满了,你就不能推,如果你推了,你就会得到堆栈溢出错误。
当你在堆栈中分配了太多内存时,就会出现堆栈溢出。例如,在前面提到的递归中。
有些实现优化了某些形式的递归。特别是尾递归。尾递归例程是一种例程,其中递归调用作为例程所做的最后一件事出现。这样的例行调用被简化为跳转。
有些实现甚至实现了自己的递归堆栈,因此它们允许递归继续进行,直到系统内存耗尽。
你可以尝试的最简单的事情就是如果可以的话增加你的堆栈大小。如果不能这样做,那么第二种方法是查看是否有什么东西明显导致堆栈溢出。试着在调用前后打印一些东西到例程中。这可以帮助您找到失败的例程。
堆栈有一个空间限制,这取决于操作系统。正常的大小是8mb(在Ubuntu (Linux)中,你可以用$ ulimit -u检查这个限制,在其他操作系统中也可以类似地检查)。任何程序都在运行时使用堆栈,但要完全了解何时使用堆栈,您需要检查汇编语言。例如,在x86_64中,堆栈用于:
在进行过程调用时保存返回地址 保存本地变量 保存特殊寄存器以便稍后恢复它们 向过程调用传递参数(大于6) 其他:随机未使用的堆栈基础,金丝雀值,填充,…等。
如果您不知道x86_64(一般情况下),您只需要知道您所使用的特定高级编程语言何时编译这些操作。例如在C语言中:
→函数调用 (2)→函数调用中的局部变量(包括main) (3)→函数调用中的局部变量(不是main) →函数调用 (5)→通常是一个函数调用,通常与堆栈溢出无关。
因此,在C语言中,只有局部变量和函数调用使用堆栈。造成堆栈溢出的两种(唯一的?)方法是:
在main或任何函数中声明过大的局部变量(int array[10000][10000];) 深度递归或无限递归(同时调用太多函数)。
要避免StackOverflowError,您可以:
检查局部变量是否太大(1mb数量级)→使用堆(malloc/calloc调用)或全局变量。 检查无限递归→你知道该怎么做…正确的! 检查常规的深度递归→最简单的方法是将实现更改为迭代。
还要注意全局变量、包含库等等……不要使用堆栈。
只有当上述方法不起作用时,才可以在特定的操作系统上将堆栈大小更改为最大值。例如Ubuntu: ulimit -s 32768 (32 MB)。(这从来都不是我的任何堆栈溢出错误的解决方案,但我也没有太多经验。)
我省略了C语言中的特殊和/或非标准情况(例如alloc()和类似的用法),因为如果你正在使用它们,你应该已经确切地知道你在做什么。
推荐文章
- List、List<?>, List<T>, List<E>, List<Object>
- 在Java中转换字符串到日历对象
- 在java中使用wait()和notify()的简单场景
- 增强的for循环中的Null检查
- c# vs Java Enum(适合c#新手)
- 如何在不强制转换的情况下将double转换为long ?
- for循环和for-each循环在性能上有区别吗?
- 你如何比较两个版本的字符串在Java?
- 为什么在Java和。net中不能修改字符串?
- java.util.Random真的那么随机吗?我怎么能生成52!(阶乘)可能的序列?
- ZoneOffset之间的区别是什么。UTC和ZoneId.of(“UTC”)?
- 类未找到:IntelliJ中的空测试套件
- 将JAR文件添加到Spark作业- Spark -submit
- REST API - dto还是不是?
- JavaFX应用程序图标