Memory leaks 什么是堆栈溢出错误?

Memory leaks 什么是堆栈溢出错误?,memory-leaks,exception-handling,out-of-memory,stack-overflow,Memory Leaks,Exception Handling,Out Of Memory,Stack Overflow,什么是StackOverflower错误,是什么原因导致的,我应该如何处理它们?正如您所说,您需要显示一些代码:- 当函数调用嵌套太深时,通常会发生堆栈溢出错误。参见线程中的一些例子,了解这是如何发生的,但是在这个问题中,答案故意导致堆栈溢出。堆栈溢出通常是通过嵌套函数调用太深来调用的,使用递归时尤其容易,例如,一个调用自身或在堆栈上分配大量内存的函数,在堆栈上使用堆会更合适。如果您有如下函数: int foo() { // more stuff foo(); } 然后foo会

什么是StackOverflower错误,是什么原因导致的,我应该如何处理它们?

正如您所说,您需要显示一些代码:-


当函数调用嵌套太深时,通常会发生堆栈溢出错误。参见线程中的一些例子,了解这是如何发生的,但是在这个问题中,答案故意导致堆栈溢出。

堆栈溢出通常是通过嵌套函数调用太深来调用的,使用递归时尤其容易,例如,一个调用自身或在堆栈上分配大量内存的函数,在堆栈上使用堆会更合适。

如果您有如下函数:

int foo()
{
    // more stuff
    foo();
}

然后foo会不断地调用自己,越来越深,当用来跟踪所使用函数的空间被填满时,就会出现堆栈溢出错误。

参数和局部变量在堆栈上分配有引用类型,对象位于堆上,堆栈中的变量引用堆上的该对象。堆栈通常位于地址空间的上端,当它用完时,它会朝向地址空间的底部,即朝向零

public class Example3 {

public static void main(String[] args) {

    main(new String[1]);

}
您的进程还有一个堆,它位于进程的底部。当您分配内存时,这个堆可以向地址空间的上端增长。正如你所看到的,堆有可能与堆发生碰撞,有点像构造板块

堆栈溢出的常见原因是错误的递归调用。通常,这是因为递归函数没有正确的终止条件,所以它会永远调用自己。或者,当终止条件很好时,可能是因为在执行之前需要太多递归调用

然而,通过GUI编程,可以生成间接递归。例如,您的应用程序可能正在处理绘制消息,并且在处理这些消息时,它可能会调用导致系统发送另一条绘制消息的函数。这里您没有显式地调用自己,但OS/VM已经为您完成了调用

要处理它们,您需要检查代码。如果你有自己调用的函数,那么检查你是否有终止条件。如果有,则检查调用函数时是否至少修改了一个参数,否则递归调用的函数将没有可见的更改,终止条件也将无效。还要注意,在达到有效终止条件之前,堆栈空间可能会耗尽内存,因此请确保方法能够处理需要更多递归调用的输入值


如果没有明显的递归函数,请检查是否调用了任何库函数,这些库函数会间接导致调用上述隐式函数。

堆栈溢出的最常见原因是递归过深或无限大。如果这是您的问题,可以帮助您理解问题。

堆栈溢出的确切含义是:堆栈溢出。通常,程序中有一个包含本地范围变量和地址的堆栈,当例程执行结束时,这些变量和地址将返回。该堆栈往往是内存中某个位置的固定内存范围,因此它可以包含的值有限

如果堆栈为空,则无法弹出,否则将出现堆栈下溢错误

public class Example3 {

public static void main(String[] args) {

    main(new String[1]);

}
如果堆栈已满,则无法推送,否则将出现堆栈溢出错误

public class Example3 {

public static void main(String[] args) {

    main(new String[1]);

}
因此,如果在堆栈中分配太多,则会出现堆栈溢出。例如,在提到的递归中

有些实现优化了某些形式的递归。特别是尾部递归。尾部递归例程是例程的一种形式,其中递归调用作为例程所做的最后一件事出现。这样的例行调用被简化为一个跳转

有些实现甚至实现了自己的递归堆栈,因此它们允许递归继续,直到系统内存耗尽

如果可以的话,最简单的方法就是增加堆栈大小。如果您不能做到这一点,那么第二个最好的方法就是查看是否有明显的原因导致堆栈溢出。试着在调用之前和之后打印一些东西到例程中。这有助于您找出失败的例程。

StackOverflowerError与OutOfMemoryError一样位于堆栈中

无界递归调用会导致堆栈空间被耗尽

以下示例生成StackOverflowerError:


如果递归调用是有界的,以防止未完成的内存内调用(以字节为单位)的总和超过堆栈(以字节为单位)的大小,则可以避免StackOverflowerError。

下面是一个用于反转单链表的递归算法示例。在具有以下spec 4G内存、Intel Core i5 2.3GHz CPU、64位Windows 7的笔记本电脑上,此功能 对于大小接近10000的链接列表,将遇到StackOverflow错误

我的观点是,我们应该明智地使用递归,始终考虑系统的规模。 通常,递归可以转换为迭代程序,这样可以更好地扩展。页面底部给出了同一算法的一个迭代版本,它在9毫秒内反转大小为100万的单链表

    private static LinkedListNode doReverseRecursively(LinkedListNode x, LinkedListNode first){

    LinkedListNode second = first.next;

    first.next = x;

    if(second != null){
        return doReverseRecursively(first, second);
    }else{
        return first;
    }
}

public static LinkedListNode reverseRecursively(LinkedListNode head){
    return doReverseRecursively(null, head);
}
相同算法的迭代版本:


堆栈溢出这一术语经常被使用,但用词不当;攻击不会使堆栈溢出,而是使堆栈上的缓冲区溢出


-为了描述这一点,首先让我们了解局部变量和对象是如何存储的

局部变量存储在堆栈中:

如果你看了这张图片,你应该能够理解事情是如何运作的

当Java应用程序调用函数调用时,会在调用堆栈上分配堆栈帧。堆栈框架包含被调用方法的参数、其本地参数和方法的返回地址。返回地址表示执行点,在调用的方法返回后,程序将从该执行点继续执行。如果没有空间容纳新的堆栈帧,则Java虚拟机JVM会抛出StackOverflowerError

最常见的可能耗尽Java应用程序堆栈的情况是递归。在递归中,方法在执行过程中调用自身。递归被认为是一种功能强大的通用编程技术,但必须谨慎使用,以避免堆栈溢出错误

抛出StackOverflowerError的示例如下所示:

StackOverflowerErrorExample.java:

公共类StackOverflowerRoreSample{ 公共静态void recursivePrintint num{ System.out.printlnNumber:+num; 如果num==0 回来 其他的 递归打印++num; } 公共静态无效字符串[]args{ StackOverflowerRoreSample.recursivePrint1; } } 在本例中,我们定义了一个名为recursivePrint的递归方法,该方法打印一个整数,然后调用自身,并将下一个连续整数作为参数。递归结束,直到我们传入0作为参数。然而,在我们的示例中,我们从1及其递增的跟随者传入参数,因此,递归永远不会终止

下面显示了使用-Xss1M标志执行的示例,该标志将线程堆栈的大小指定为1MB:

编号:1 编号:2 电话:3 ... 电话:6262 电话:6263 电话:6264 电话:6265 电话:6266 主线程java.lang.StackOverflowerr中出现异常 在java.io.PrintStream.writePrintStream.java:480 在sun.nio.cs.streamncoder.writebytes-StreamEncoder.java:221 位于sun.nio.cs.streamncoder.implFlushBufferstreamncoder.java:291 位于sun.nio.cs.streamncoder.flushBufferstreamncoder.java:104 位于java.io.OutputStreamWriter.flushBufferOutputStreamWriter.java:185 在java.io.PrintStream.writePrintStream.java:527 在java.io.PrintStream.printPrintStream.java:669 在java.io.PrintStream.printlnPrintStream.java:806 在StackOverflowerErrorExample.recursivePrintStackOverflowerErrorExample.java:4 在stackOverflowerErrorExample.recursivePrintStackOverflowerErrorExample.java:9 在stackOverflowerErrorExample.recursivePrintStackOverflowerErrorExample.java:9 在stackOverflowerErrorExample.recursivePrintStackOverflowerErrorExample.java:9 ... 根据JVM的初始配置,结果可能会有所不同,但最终会抛出StackOverflowerError。这个例子是一个很好的例子,说明了如果不小心实现递归,它会导致问题

如何处理堆栈溢出错误

最简单的解决方案是仔细检查堆栈跟踪和 检测行号的重复模式。这些行号 指示递归调用的代码。一旦你发现这些 行,您必须仔细检查代码并理解 递归永远不会终止

这里有一个例子

public static void main(String[] args) {
    System.out.println(add5(1));
}

public static int add5(int a) {
    return add5(a) + 5;
}
StackOverflowerError基本上是当你试图做某件事时,它很可能会调用自己,然后继续无限大,或者直到它给出StackOverflowerError

add5a将调用自身,然后再次调用自身,依此类推。

StackOverflower错误在java中是一个运行时错误

当超过JVM分配的调用堆栈内存量时,将抛出该函数

抛出StackOverflowerError的常见情况是调用堆栈由于过度的深度递归或无限递归而超出

例如:

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)
在上述情况下,可以通过进行编程更改来避免这种情况。 但是,如果程序逻辑正确并且仍然发生,那么堆栈大小需要增加

这是java.lang.StackOverflower的典型案例。。。该方法在doub中无出口地递归调用自身 leValue、floatValue等

Rational.java
在紧急情况下,下面的情况会带来堆栈溢出错误

public class Example3 {

public static void main(String[] args) {

    main(new String[1]);

}

}

堆栈的空间限制取决于操作系统,Ubuntu中的正常大小为8MB,您可以使用$ulimit-u检查该限制,其他操作系统也可以类似地检查该限制。任何程序都会在运行时使用堆栈,但要完全了解何时使用堆栈,需要检查汇编语言。例如,在x86_64中,堆栈用于:

在进行过程调用时保存返回地址 保存局部变量 保存特殊寄存器以在以后还原它们 向超过6个的过程调用传递参数 其他:随机未使用的堆栈基、加那利值、填充。。。等 如果您不知道x86_64的正常情况,您只需要知道您正在使用的特定高级别编程语言何时编译这些操作。例如,在C中:

1.→ 函数调用 2.→ 函数调用中的局部变量,包括main 3.→ 函数调用中的局部变量不是main 4.→ 函数调用 5.→ 通常是函数调用,它通常与堆栈溢出无关。 因此,在C中,只有局部变量和函数调用使用堆栈。2独特的?使堆栈溢出的方法有:

在main或其调用的int数组[10000][10000]的任何函数中声明过大的局部变量; 一个非常深或无限的递归同时调用了太多的函数。 要避免堆栈溢出错误,您可以:

检查局部变量是否太大(1 MB)→ 使用堆malloc/calloc调用或全局变量

检查无限递归→ 你知道怎么做。。。纠正它

检查是否存在正常的太深递归→ 最简单的方法是将实现更改为迭代

还要注意全局变量,包括库等。。。不要使用堆栈

仅当上述操作不起作用时,在特定操作系统上将堆栈大小更改为最大值。以Ubuntu为例:$ulimit-s3276832MB。这从来不是我的任何堆栈溢出错误的解决方案,但我也没有太多经验

我省略了C中的特殊和/或非标准情况,例如alloc和类似的用法,因为如果您正在使用它们,您应该已经确切地知道您在做什么


干杯

简单的Java示例,由于错误的递归调用而导致Java.lang.StackOverflowerError

class Human {
    Human(){
        new Animal();       
    }
}

class Animal extends Human {
    Animal(){
        super();
    }
}

public class Test01 {
    public static void main(String[] args) {
        new Animal();
    }
}


哦,没有看到Java标签,从这里的原始海报:嵌套函数太深,在什么地方?其他功能?还有:如何将内存分配给堆栈或堆,因为,你知道,我显然在不知情的情况下做了其中一件事。@Ziggy:是的,如果一个函数调用另一个函数,而另一个函数调用另一个函数,等等,在许多级别之后,你的程序将出现堆栈溢出。[continues][…continued]在Java中,您不能直接从堆栈分配内存,而在C中,您可以,这将是需要注意的,所以这不太可能是原因。在Java中,所有直接分配都来自堆,通过使用new.@ChrisJester-Young,如果一个方法中有100个局部变量,所有这些变量都会毫无例外地进入堆栈,这不是真的吗?我完全想添加代码,但由于我不知道是什么导致堆栈溢出,我不确定要添加什么代码。添加所有的代码都是蹩脚的,不是吗?你的项目是开源的吗?如果是这样,只需创建一个Sourceforge或github帐户,并将您的所有代码上传到那里即可:-这听起来是个好主意,但我是一个不知道该上传什么的人。比如,我正在导入我正在扩展的类的库等等。。。对我来说都是未知的。哦,男人:糟糕的时光。原始海报:嘿,这太棒了。所以递归总是导致堆栈溢出?或者其他事情也能对他们负责?不幸的是,我正在使用一个库。。。但不是我能理解的。哈哈哈,就是这样:虽然点<100{addmouselisters;moveball;checkforcollision;pausespeed;}哇,我没有意识到我最终会有一堆鼠标侦听器,我感到很跛脚。。。谢谢大家!不,如果您在上查找Wikipedia文章,堆栈溢出也可能是由于变量太大而无法在堆栈上分配。应该指出的是,几乎不可能处理堆栈溢出错误。在大多数环境中,要处理错误,需要在堆栈上运行代码,如果没有更多的堆栈空间,这是很困难的。@JB King:不适用于Java,因为Java在堆栈上只保留基本类型和引用。所有大的数组和对象都在堆上。错了。您的函数是尾部递归的。大多数编译语言都有尾部递归优化。这意味着递归将简化为一个简单的循环,在某些系统上,使用这段代码永远不会出现堆栈溢出。Cheery,哪些非函数语言支持尾部递归?@banister和的一些实现javascript@horseyguyScala支持Tail r
ecursion。这捕获了可能导致堆栈溢出的本质。很好。有堆栈下溢这样的事情吗?堆栈下溢可能发生在程序集弹出比你推的更多的情况下,尽管在编译语言中这几乎是不可能的。我不确定,您可能会找到一个支持负大小的C的alloca实现。通常程序中有一个包含局部范围变量的堆栈->不,每个线程都有自己的堆栈,其中包含包含局部变量的每个方法调用的堆栈帧..我认为使用JVM,实际上,你的笔记本电脑是什么规格并不重要。当使用windows时,-Xss参数只对新线程生效,而java中的线程大小很小,在某些java版本中似乎存在缺陷。有些时候,比如很多递归调用,你会遇到这个问题。您可以按循环重新设计代码。您可以在这个url中找到通用的设计模式来实现它:一种不明显的方式:添加行newobject{{getClass.newInstance;};对于某些静态上下文,例如main方法。无法从实例上下文运行,仅抛出InstanceException。
    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)
public class Example3 {

public static void main(String[] args) {

    main(new String[1]);

}
class Human {
    Human(){
        new Animal();       
    }
}

class Animal extends Human {
    Animal(){
        super();
    }
}

public class Test01 {
    public static void main(String[] args) {
        new Animal();
    }
}