想要详细了解 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"); } }
这个程序非常简单,执行代码如下:
main()首先调用方法
main() 方法调用a() 方法。在方法内部a() ,整型变量“x”被初始化为值 0。
a() 方法依次调用b()方法。在方法内部b() ,构造 Car 对象并将其分配给变量“y”。
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)
图 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 和某些其他平台没有影响。跨平台缺乏一致性使得该选项作为通用解决方案不太可靠。作为最佳实践,建议选择在所有平台上一致工作的解决方案,以确保应用程序的稳定性和可靠性。文章来源:https://www.toymoban.com/diary/java/305.html
到此这篇关于StackOverFlow 错误:原因和解决方案的文章就介绍到这了,更多相关内容可以在右上角搜索或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!