java 栈溢出_Java栈溢出--StackOverflowError

StackOverflowError原因

Java 里的 StackOverflowError。抛出这个错误表明应用程序因为深递归导致栈被耗尽了。每当java程序启动一个新的线程时,java虚拟机会为他分配一个栈,java栈以帧为单位保持线程运行状态;当线程调用一个方法是,jvm压入一个新的栈帧到这个线程的栈中,只要这个方法还没返回,这个栈帧就存在。 如果方法的嵌套调用层次太多(如递归调用),随着java栈中的帧的增多,最终导致这个线程的栈中的所有栈帧的大小的总和大于-Xss设置的值,而产生生StackOverflowError溢出异常。

StackOverflowError异常

StackOverflowError 是 VirtualMachineError 的扩展类,VirtualMachineError 表明 JVM 中断或者已经耗尽资源,无法运行。而且,VirtualMachineError类扩展自 Error 类,这个类用于指出那些应用程序不需捕获的严重问题。因为这些错误是在可能永远不会发生的异常情况下产生,所以方法中没有在它的 throw 语句中声明。

StackOverflowError从 Java 1.0开始出现。

StackOverflowError的结构

构造函数

* StackOverflowError()

Creates an instance of the StackOverflowError class, setting null as its message.

* StackOverflowError(String s)

Creates an instance of the StackOverflowError class, using the specified string as message. The string argument indicates the name of the class that threw the error.

Java 里的 StackOverflowError

Java 应用程序唤起一个方法调用时就会在调用栈上分配一个栈帧, 这个栈帧包含引用方法的参数,本地参数,以及方法的返回地址。

这个返回地址是被引用的方法返回后程序能够继续执行的执行点。如果没有一个新的栈帧所需空间,Java 虚拟机就会抛出 StackOverflowError。

最常见的可能耗光 Java 应用程序的栈的场景是程序里的递归。递归时一个方法在执行过程中会调用自己。 递归被认为是一个强大的多用途编程技术,为了避免出现 StackOverflowError,使用时必须特别小心。

下面是一个抛出StackOverflowError的例子:

StackOverflowErrorExample.java:

/**

*

*

* @author Sunny

* @version 1.0

* @taskId:

* @createDate 2018/09/05 15:48

* @see com.sunny.jdk.oom

*/

public class StackOverflowErrorExample {

public static void recursivePrint(int num) {

System.out.println("Number: " + num);

if(num == 0)

return;

else

recursivePrint(++num);

}

public static void main(String[] args) {

StackOverflowErrorExample.recursivePrint(1);

}

}

在示例中我们定义了一个称为 recursivePrint 的递归方法,里面打印了一个整数,用下一个连续的整数作为参数调用自己。调用方法时如果传入参数是 0 ,递归结束。但在我们的示例里是从 1 开始打印数字,所以这个递归将永远不会中止。

使用 -Xss1M 标志来指定线程栈的大小等于 1MB,执行结果如下所示:

Number: 1

Number: 2

Number: 3

...

Number: 6262

Number: 6263

Number: 6264

Number: 6265

Number: 6266

Exception in thread "main" java.lang.StackOverflowError

at java.io.PrintStream.write(PrintStream.java:480)

at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)

at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)

at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)

at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)

at java.io.PrintStream.write(PrintStream.java:527)

at java.io.PrintStream.print(PrintStream.java:669)

at java.io.PrintStream.println(PrintStream.java:806)

at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:4)

at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)

at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)

at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)

...

依赖于 JVM 的初始配置结果可能会有差异,但是最终会抛出 StackOverflowError。这是演示如果没有小心的实现递归如何导致问题的一个非常好的例子。

接下来的例子演示类之间有循环关系时的风险:

StackOverflowErrorToStringExample.java:

package com.sunny.jdk.oom;

class A {

private int aValue;

private B bInstance = null;

public A() {

aValue = 0;

bInstance = new B();

}

@Override

public String toString() {

return "";

}

}

class B {

private int bValue;

private A aInstance = null;

public B() {

bValue = 10;

aInstance = new A();

}

@Override

public String toString() {

return "";

}

}

/**

* StackOverflowError

*

* @author Sunny

* @version 1.0

* @taskId:

* @createDate 2018/09/05 10:41

* @see com.sunny.jdk.oom

*/

public class StackOverflowErrorToStringExample {

public static void main(String[] args) {

A obj = new A();

System.out.println(obj.toString());

}

}

在这个例子中我们定义了两个类,A 和 B。类 A 包含一个 B 类的实例,同时,类 B 也包含 A 类的一个实例。这样,在两个类之间就有了一个循环依赖。而且,每一个 toString 方法从另一个类引用了相应的 toString 方法,等等,最终结果是抛出 StackOverflowError。

执行结果如下所示:

Exception in thread "main" java.lang.StackOverflowError

at com.sunny.jdk.oom.B.(StackOverflowErrorToStringExample.java:24)

at com.sunny.jdk.oom.A.(StackOverflowErrorToStringExample.java:9)

at com.sunny.jdk.oom.B.(StackOverflowErrorToStringExample.java:24)

at com.sunny.jdk.oom.A.(StackOverflowErrorToStringExample.java:9)

at com.sunny.jdk.oom.B.(StackOverflowErrorToStringExample.java:24)

at com.sunny.jdk.oom.A.(StackOverflowErrorToStringExample.java:9)

at com.sunny.jdk.oom.B.(StackOverflowErrorToStringExample.java:24)

at com.sunny.jdk.oom.A.(StackOverflowErrorToStringExample.java:9)

at com.sunny.jdk.oom.B.(StackOverflowErrorToStringExample.java:24)

at com.sunny.jdk.oom.A.(StackOverflowErrorToStringExample.java:9)

at com.sunny.jdk.oom.B.(StackOverflowErrorToStringExample.java:24)

at com.sunny.jdk.oom.A.(StackOverflowErrorToStringExample.java:9)

at com.sunny.jdk.oom.B.(StackOverflowErrorToStringExample.java:24)

at com.sunny.jdk.oom.A.(StackOverflowErrorToStringExample.java:9)

at com.sunny.jdk.oom.B.(StackOverflowErrorToStringExample.java:24)

at com.sunny.jdk.oom.A.(StackOverflowErrorToStringExample.java:9)

at com.sunny.jdk.oom.B.(StackOverflowErrorToStringExample.java:24)

at com.sunny.jdk.oom.A.(StackOverflowErrorToStringExample.java:9)

at com.sunny.jdk.oom.B.(StackOverflowErrorToStringExample.java:24)

at com.sunny.jdk.oom.A.(StackOverflowErrorToStringExample.java:9)

at com.sunny.jdk.oom.B.(StackOverflowErrorToStringExample.java:24)

at com.sunny.jdk.oom.A.(StackOverflowErrorToStringExample.java:9)

...

如何处理 StackOverflowError

最简单的解决方案是仔细检查输出信息中的栈路径,查明模式重复的代码行号。这些行号对应的代码被递归调用了。确认这些行后,你必须小心的检查你的代码,弄清楚为什么递归永远不结束。

如果你确认递归实现是正确的,为了允许大量的调用,你可以增加栈的大小。依赖于安装的 Java 虚拟机,默认的线程栈大小可能是 512KB 或者 1MB。你可以使用 -Xss 标识来增加线程栈的大小。这个标识即可以通过项目的配置也可以通过命令行来指定。-Xss 参数的格式:

-Xss[g|G|m|M|k|K]

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值