StackOverFlow 错误:原因和解决方案

20,116次阅读
没有评论

共计 3696 个字符,预计需要花费 10 分钟才能阅读完成。

想要详细了解 JVM 项目中 StackOverFlowError 的潜在原因和解决方案吗?查看这篇文章以了解更多信息。

StackOverFlowError是常见的 JVM 错误之一。在这篇博文中,我们将了解线程堆栈的内部机制、触发 StackOverFlowError 此错误的原因以及解决此错误的潜在解决方案。

为了更深入地了解StackOverFlowError,让我们回顾一下这个简单的程序:

public class SimpleExample {public static void main(String args[]) {a()
      }

      public static void a() {

            int x = 0;
            b();}

      public static void b() {Car y = new Car();
            c();}

      public static void c() {

            float z = 0f;
      System.out.println("Hello");
      }
}

这个程序非常简单,执行代码如下:

  1.  main()首先调用方法

  2.  main() 方法调用a() 方法。在方法内部a(),整型变量“x”被初始化为值 0。

  3.  a() 方法依次调用 b() 方法。在方法内部b(),构造 Car 对象并将其分配给变量“y”。

  4.  b() 方法依次调用该 c() 方法。在该 c() 方法内部,浮点变量“z”被初始化为值 0。

现在,让我们回顾一下执行上述简单程序时幕后发生的情况。应用程序中的每个线程都有自己的堆栈。每个堆栈有多个堆栈帧。线程将其正在执行的方法、原始数据类型、对象指针以及返回值按照它们执行的顺序添加到其堆栈帧中。

文章来源地址 https://www.toymoban.com/diary/java/305.html

线程的堆栈帧

图 1:线程的堆栈帧

步骤 #1: main() 方法被推入应用程序线程的堆栈中。

步骤 #2: a() 方法被推入应用程序线程的堆栈中。在 a()  方法中,原始数据类型“int”定义为值 0 并分配给变量 x。该信息也被推送到同一堆栈帧中。请注意,这两个数据(即“0”和变量“x”)都被推入线程的堆栈帧中。

步骤 #3: b() 方法被推入线程的堆栈。在该 b()  方法中,创建了 Car 对象并将其分配给变量“y”。这里需要注意的一个关键点是“Car”对象是在堆中创建的,而不是在线程的堆栈中创建的。只有 Car 对象的引用(即 y)存储在线程的堆栈帧中。

步骤 #4:c() 方法被推入线程的堆栈中。在 c()  方法中,原始数据类型“float”定义为值 0f 并分配给变量 z。该信息也被推送到同一堆栈帧中。请注意,这两个数据(即“0f”和变量“z”)都被推入线程的堆栈帧中。

每个方法执行完成后,该方法和存储在堆栈帧中的变量 / 对象指针将被删除,如图 2 所示。

执行方法后线程的堆栈帧

图 2:执行方法后线程的堆栈帧

是什么原因造成的 StackOverflowError?

正如您所看到的,线程的堆栈存储它正在执行的方法、原始数据类型、变量、对象指针和返回值。所有这些都会消耗内存。如果线程的堆栈大小超出了分配的内存限制,则会StackOverflowError 抛出异常。让我们看一下下面的错误程序,它将导致  StackOverflowError

public class SOFDemo {public static void a() {// Buggy line. It will cause method a() to be called infinite number of times.
        a();}
    public static void main(String args[]) {a();
    }
}

在这个程序中,main() 方法调用 a()  方法。a() 方法递归调用自身。此实现将导致  a() 方法被无限次调用。在这种情况下,a() 方法将被无限次添加到线程的堆栈帧中。因此,经过几千次迭代后,将超出线程的堆栈大小限制。一旦超出堆栈大小限制,将导致  StackOverflowError

Exception in thread "main" java.lang.StackOverflowError
    at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)
    at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)
    at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)
    at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)
    at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)
    at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)
    at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)
    at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)

StackOverflowError 进展

图 3:StackOverflowError 进展

有什么解决办法 StackOverflowError?

有几个策略需要解决  StackOverflowError

1. 修复代码

由于非终止递归调用(如上例所示),线程堆栈大小可能会增长到很大。在这些情况下,您必须修复导致递归循环的源代码。当 StackOverflowError 抛出时,它将打印递归执行的代码的堆栈跟踪。此代码是开始调试和解决问题的良好指针。在上面的例子中,就是a()  方法。

2. 增加线程堆栈大小 (-Xss)

需要增加线程的堆栈大小可能有合理的原因。也许线程必须执行大量方法或线程正在执行的方法中 / 创建的大量局部变量?在这种情况下,您可以使用 JVM 参数增加线程的堆栈大小:-Xss。启动应用程序时需要传递此参数。例子:

-Xss2m

这会将线程的堆栈大小设置为 2 mb。

这可能会带来一个问题:默认线程的堆栈大小是多少?默认线程堆栈大小因操作系统、Java 版本和供应商而异。

JVM 版本

线程堆栈大小

Sparc 32 位 JVM

512k

Sparc 64 位 JVM

1024k

x86 Solaris/Linux 32 位 JVM

320K

x86 Solaris/Linux 64 位 JVM

1024K

Windows 32 位 JVM

320K

Windows 64 位 JVM

1024K

3. 具有自定义堆栈大小的线程

另一种缓解方法 StackOverflowError 是利用 Java 的线程构造函数,它允许您为各个线程指定自定义堆栈大小。这个构造函数可以在 Java 文档 (https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#Thread-java.lang.ThreadGroup-java.lang.Runnable-java.lang.String-long-) 中找到。虽然此选项提供了为每个线程设置特定堆栈大小的灵活性,但请务必注意,其有效性可能因不同平台而异。

Thread thread = new Thread(null, runnable, "CustomThread", customStackSize);
thread.start();

然而,重要的是要意识到设置参数的影响在所有平台上 stackSize 可能并不
一致。Java 文档指出:“在某些平台上,stackSize 参数的值可能没有任何影响 虚拟机可以自由地将 stackSize 参数视为建议。”

在我们自己的测试中,我们发现使用自定义堆栈大小调用此构造函数对 Windows 和某些其他平台没有影响。跨平台缺乏一致性使得该选项作为通用解决方案不太可靠。作为最佳实践,建议选择在所有平台上一致工作的解决方案,以确保应用程序的稳定性和可靠性。

到此这篇关于 StackOverFlow 错误:原因和解决方案的文章就介绍到这了, 更多相关内容可以在右上角搜索或继续浏览下面的相关文章,希望大家以后多多支持 TOY 模板网!

原文地址:https://www.toymoban.com/diary/java/305.html

如若转载,请注明出处:如若内容造成侵权 / 违法违规 / 事实不符,请联系站长进行投诉反馈,一经查实,立即删除!

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于1970-01-01发表,共计3696字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)