Java栈溢出--StackOverflowError

StackOverflowError原因

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

StackOverflowError异常

StackOverflowErrorVirtualMachineError 的扩展类,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:

/**
 * <Description> <br>
 *
 * @author Sunny<br>
 * @version 1.0<br>
 * @taskId: <br>
 * @createDate 2018/09/05 15:48 <br>
 * @see com.sunny.jdk.oom <br>
 */
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 "<" + aValue + ", " + bInstance + ">";
    }
}

class B {
    private int bValue;
    private A aInstance = null;
    
    public B() {
        bValue = 10;
        aInstance = new A();
    }
    
    @Override
    public String toString() {
        return "<" + bValue + ", " + aInstance + ">";
    }
}

/**
 * <Description> StackOverflowError<br>
 *
 * @author Sunny<br>
 * @version 1.0<br>
 * @taskId: <br>
 * @createDate 2018/09/05 10:41 <br>
 * @see com.sunny.jdk.oom <br>
 */
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.<init>(StackOverflowErrorToStringExample.java:24)
    at com.sunny.jdk.oom.A.<init>(StackOverflowErrorToStringExample.java:9)
    at com.sunny.jdk.oom.B.<init>(StackOverflowErrorToStringExample.java:24)
    at com.sunny.jdk.oom.A.<init>(StackOverflowErrorToStringExample.java:9)
    at com.sunny.jdk.oom.B.<init>(StackOverflowErrorToStringExample.java:24)
    at com.sunny.jdk.oom.A.<init>(StackOverflowErrorToStringExample.java:9)
    at com.sunny.jdk.oom.B.<init>(StackOverflowErrorToStringExample.java:24)
    at com.sunny.jdk.oom.A.<init>(StackOverflowErrorToStringExample.java:9)
    at com.sunny.jdk.oom.B.<init>(StackOverflowErrorToStringExample.java:24)
    at com.sunny.jdk.oom.A.<init>(StackOverflowErrorToStringExample.java:9)
    at com.sunny.jdk.oom.B.<init>(StackOverflowErrorToStringExample.java:24)
    at com.sunny.jdk.oom.A.<init>(StackOverflowErrorToStringExample.java:9)
    at com.sunny.jdk.oom.B.<init>(StackOverflowErrorToStringExample.java:24)
    at com.sunny.jdk.oom.A.<init>(StackOverflowErrorToStringExample.java:9)
    at com.sunny.jdk.oom.B.<init>(StackOverflowErrorToStringExample.java:24)
    at com.sunny.jdk.oom.A.<init>(StackOverflowErrorToStringExample.java:9)
    at com.sunny.jdk.oom.B.<init>(StackOverflowErrorToStringExample.java:24)
    at com.sunny.jdk.oom.A.<init>(StackOverflowErrorToStringExample.java:9)
    at com.sunny.jdk.oom.B.<init>(StackOverflowErrorToStringExample.java:24)
    at com.sunny.jdk.oom.A.<init>(StackOverflowErrorToStringExample.java:9)
    at com.sunny.jdk.oom.B.<init>(StackOverflowErrorToStringExample.java:24)
    at com.sunny.jdk.oom.A.<init>(StackOverflowErrorToStringExample.java:9)
    ...

如何处理 StackOverflowError

 最简单的解决方案是仔细检查输出信息中的栈路径,查明模式重复的代码行号。这些行号对应的代码被递归调用了。确认这些行后,你必须小心的检查你的代码,弄清楚为什么递归永远不结束。
 如果你确认递归实现是正确的,为了允许大量的调用,你可以增加栈的大小。依赖于安装的 Java 虚拟机,默认的线程栈大小可能是 512KB 或者 1MB。你可以使用 -Xss 标识来增加线程栈的大小。这个标识即可以通过项目的配置也可以通过命令行来指定。-Xss 参数的格式:

-Xss<size>[g|G|m|M|k|K]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351

推荐阅读更多精彩内容

  • Java8张图 11、字符串不变性 12、equals()方法、hashCode()方法的区别 13、...
    Miley_MOJIE阅读 3,698评论 0 11
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,221评论 11 349
  • 一、运行时数据区域 Java虚拟机管理的内存包括几个运行时数据内存:方法区、虚拟机栈、本地方法栈、堆、程序计数器,...
    kennethan阅读 2,133评论 1 91
  • 2018年8月1日 星期三 晴 今天是八一建军节,宝爸的战友们早早就约好,今晚一起聚会庆祝八一。 ...
    双眼皮的小猪阅读 318评论 0 0
  • 〖放下〗 ——〈开灯心语〉 想要前进, 就要放下 ——原来的设定; 想要安宁, 就该放下 ——那千年的无明…...
    开灯教育_苏州阅读 1,086评论 0 7