什么实际上导致堆栈溢出错误?

这个问题在这里已经有了答案:

  • 什么是StackOverflowError? 13个答案

  • 看起来你认为一个stackoverflow错误就像本地程序中的缓冲区溢出异常,当存在写入未分配给缓冲区的内存的风险,从而破坏其他一些内存位置时。 事实并非如此。

    JVM为每个线程的每个堆栈分配一个给定的内存,如果尝试调用某个方法来填充此内存,则JVM将引发错误。 就像它试图在长度为N的数组的索引N处写入一样。不会发生内存损坏。 堆栈不能写入堆中。

    StackOverflowError是Stack的一个OutOfMemoryError堆栈:它只是表示没有更多的内存可用。

    虚拟机错误说明(第6.3节)

    StackOverflowError :Java虚拟机实现已经耗尽了线程的堆栈空间,通常是因为线程正在执行无限数量的递归调用,因为执行程序中存在错误。


    没有其他方法可以发生堆栈溢出,不仅通过递归?

    当然。 只要继续调用方法,永不返回。 但是,除非允许递归,否则您需要很多方法。 实际上,它并没有什么区别:一个堆栈帧是一个堆栈帧,不管它是递归方法之一还是不相同。

    第二个问题的答案是:当JVM尝试为下一次调用分配堆栈帧并检测到不可能时,检测到stackoverflow。 所以,什么都不会被覆盖。


    没有其他方法可以发生堆栈溢出,不仅通过递归?

    挑战接受:) StackOverflowError没有递归(挑战失败,见评论):

    public class Test
    {
        final static int CALLS = 710;
    
        public static void main(String[] args)
        {
            final Functor[] functors = new Functor[CALLS];
            for (int i = 0; i < CALLS; i++)
            {
                final int finalInt = i;
                functors[i] = new Functor()
                {
                    @Override
                    public void fun()
                    {
                        System.out.print(finalInt + " ");
                        if (finalInt != CALLS - 1)
                        {
                            functors[finalInt + 1].fun();
                        }
                    }
                };
            }
            // Let's get ready to ruuuuuuumble!
            functors[0].fun(); // Sorry, couldn't resist to not comment in such moment. 
        }
    
        interface Functor
        {
            void fun();
        }
    }
    

    使用标准javac Test.java编译并使用java -Xss104k Test 2> out 。 之后, more out会告诉你:

    Exception in thread "main" java.lang.StackOverflowError
    

    第二次尝试。

    现在这个想法更简单了。 Java中的基元可以存储在堆栈中。 所以,我们宣布很多双打,比如double a1,a2,a3... 该脚本可以为我们编写,编译和运行代码:

    #!/bin/sh
    
    VARIABLES=4000
    NAME=Test
    FILE=$NAME.java
    SOURCE="public class $NAME{public static void main(String[] args){double "
    for i in $(seq 1 $VARIABLES);
    do
        SOURCE=$SOURCE"a$i,"
    done
    SOURCE=$SOURCE"b=0;System.out.println(b);}}"
    echo $SOURCE > $FILE
    javac $FILE
    java -Xss104k $NAME
    

    而且......我有些意想不到的事情:

    #
    # A fatal error has been detected by the Java Runtime Environment:
    #
    #  SIGSEGV (0xb) at pc=0x00007f4822f9d501, pid=4988, tid=139947823249152
    #
    # JRE version: 6.0_27-b27
    # Java VM: OpenJDK 64-Bit Server VM (20.0-b12 mixed mode linux-amd64 compressed oops)
    # Derivative: IcedTea6 1.12.6
    # Distribution: Ubuntu 10.04.1 LTS, package 6b27-1.12.6-1ubuntu0.10.04.2
    # Problematic frame:
    # V  [libjvm.so+0x4ce501]  JavaThread::last_frame()+0xa1
    #
    # An error report file with more information is saved as:
    # /home/adam/Desktop/test/hs_err_pid4988.log
    #
    # If you would like to submit a bug report, please include
    # instructions how to reproduce the bug and visit:
    #   https://bugs.launchpad.net/ubuntu/+source/openjdk-6/
    #
    Aborted
    

    这是100%重复。 这与你的第二个问题有关:

    StackOverflowError在JVM实际溢出之前还是之后发生?

    所以,在OpenJDK 20.0-b12的情况下,我们可以看到JVM首先爆炸了。 但它看起来像一个错误,也许有人可以在评论中确认,因为我不确定。 我应该报告吗? 也许它已经修复了一些更新的版本......根据JVM规范链接(由JB Nizet在评论中给出)JVM应该抛出StackOverflowError ,而不是死亡:

    如果线程中的计算需要比允许的更大的Java虚拟机堆栈,Java虚拟机会引发StackOverflowError。


    第三次尝试。

    public class Test {
        Test test = new Test();
    
        public static void main(String[] args) {
            new Test();
        }
    }
    

    我们要创建新的Test对象。 所以,它的(隐式)构造函数将被调用。 但是,在此之前, Test所有成员都已初始化。 所以, Test test = new Test()首先被执行...

    我们想创建新的Test对象...

    更新:运气不好,这是递归,我在这里问了关于这个问题。

    链接地址: http://www.djcxy.com/p/14125.html

    上一篇: What actually causes a Stack Overflow error?

    下一篇: Avoiding stack overflows by allocating stack parts on the heap?