目录

  • 根本原因
  • 具体情况
  • 1.不恰当的递归方法
  • 2.两个方法循环调用
  • 3.循环调用构造方法
  • 4.程序自动循环调用toString()方法
  • 详情



根本原因

总结就是一句话请求的栈深度大于虚拟机所允许的最大深度。(详情请翻到最下面!!!)

具体情况

目前我一共遇到过四种情况下的java.lang.StackOverflowError异常,全部列举如下:

  1. 不恰当的递归方法;
  2. 两个方法循环调用;
  3. 循环调用构造方法;
  4. 程序自动循环调用toString()方法;

1.不恰当的递归方法

写过递归方法的我们都知道,对于递归方法来说,为防止其无休止地进行,必须在方法内有终止条件。比如就用递归方法实现斐波那契数列为例:

public int Fibonacci(int num){
		if(num == 1 || num == 2)
			return num;
		return Fibonacci(num - 1) + Fibonacci(num - 2);
}

当num等于1或者num等于2的时候Fibonacci()方法的递归调用就会停下来。

如果在方法内没有终止条件,比如说下面这样,就会发生StackOverflowError。

public int Fibonacci(int num) {

		return Fibonacci(num - 1) + Fibonacci(num - 2);
	}

这个是控制台打印结果:

Exception in thread "main" java.lang.StackOverflowError
	at jvm.OOM.math.Fibonacci(StackOverFlow.java:6)
	at jvm.OOM.math.Fibonacci(StackOverFlow.java:6)
	at jvm.OOM.math.Fibonacci(StackOverFlow.java:6)
	···

2.两个方法循环调用

我们写一个最简单的例子:

class Person {
	public void a() {
		b();
	}

	public void b() {
		a();
	}
}

public class StackOverFlow {
	public static void main(String[] args) {
		Person p = new Person();
		p.a();
	}
}

控制台打印结果:

Exception in thread "main" java.lang.StackOverflowError
	at jvm.OOM.Person.b(StackOverFlow.java:9)
	at jvm.OOM.Person.a(StackOverFlow.java:5)
	at jvm.OOM.Person.b(StackOverFlow.java:9)
	at jvm.OOM.Person.a(StackOverFlow.java:5)
	···

3.循环调用构造方法

class Person{
	Person p = new Person();
}

public class StackOverFlow {
	public static void main(String[] args) {
		Person p = new Person();
	}
}

控制台打印:

Exception in thread "main" java.lang.StackOverflowError
	at oom.Person.<init>(StackOverFlow.java:4)
	at oom.Person.<init>(StackOverFlow.java:4)
	at oom.Person.<init>(StackOverFlow.java:4)
	at oom.Person.<init>(StackOverFlow.java:4)
	at oom.Person.<init>(StackOverFlow.java:4)
	···

之所以会犯这个错误,是我刚开始学习synchronize关键字的时候,其中当synchronize关键字作用于代码块时,要给定一个对象,我当时就new了自身,所以导致了StackOverflowError异常。详见:Java多线程入门:线程安全与synchronized关键字

当时的错误代码如下所示:

class Shopping implements Runnable {
	private int num = 10;
	//会导致StackOverflowError异常
	Shopping shop = new Shopping();

	public void run() {
		while (true) {
			synchronized (shop) {
				if (num > 0) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() 
					+ "抢血拼到了第" + num-- + "双鞋");
				} else
					break;
			}
		}
	}
}

4.程序自动循环调用toString()方法

因为打印对象的时候会自动调用对象的toString()方法。当两个对象相互引用的时候,就会无限循环调用toString()方法,从而造成StackOverflowError异常。

class Reader {
	String name;
	String id;
	Book books;

	public Reader(String name, String id) {
		this.name = name;
		this.id = id;
	}

	public String toString() {
		return "[name=" + name + ", id=" + id + ", books=" + books + "]";
	}
}

class Book {
	String name;
	Reader readers;

	public Book(String name, Reader reader) {
		this.name = name;
		this.readers = reader;
	}

	public String toString() {
		return "[name=" + name + ", readers=" + readers + "]";
	}
}

public class StackOverFlow {
	public static void main(String[] args) {
		Reader reader = new Reader("张三", "33");
		Book book = new Book("三国志", reader);
		reader.books = book;
		System.out.println(reader);
	}
}

控制台打印结果:

Exception in thread "main" java.lang.StackOverflowError
	at java.lang.AbstractStringBuilder.append(Unknown Source)
	at java.lang.StringBuilder.append(Unknown Source)
	at java.lang.StringBuilder.<init>(Unknown Source)
	at jvm.OOM.Reader.toString(StackOverFlow.java:15)
	at java.lang.String.valueOf(Unknown Source)
	at java.lang.StringBuilder.append(Unknown Source)
	at jvm.OOM.Book.toString(StackOverFlow.java:30)
	at java.lang.String.valueOf(Unknown Source)
	at java.lang.StringBuilder.append(Unknown Source)
	at jvm.OOM.Reader.toString(StackOverFlow.java:15)
	at java.lang.String.valueOf(Unknown Source)
	at java.lang.StringBuilder.append(Unknown Source)
	at jvm.OOM.Book.toString(StackOverFlow.java:30)
	···

如果看到这里的您还有什么高见,欢迎补充哦!!!!!!!!!!今天也是奋斗的一天!!!!!!!!

详情

能翻到这里的朋友,肯定是想详细了解发生StackOverflowError的底层原因。那我们就多说一点。

首先,一般情况下我们对Java虚拟机的区域划分就是栈、堆、方法区。实际上,详细的划分如下图所示有:方法区、堆、Java虚拟机栈、本地方法栈、程序计数器。

java stackoverflow怎么办 java. lang. stackoverflowerror_Source


第二,发生StackOverflowError的数据区域就是Java虚拟机栈。在《Java虚拟机规范》中是这么描述的,

如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。

第三,为什么栈深度会大于虚拟机所允许的最大深度?
那我们就要了解Java虚拟机栈的作用。首先每个方法在执行的同时都会创建一个栈帧;第二每个方法从调用到执行完成的过程,就对应这个栈帧在Java虚拟机栈中入栈到出栈的过程;第三既然Java虚拟机栈是一个数据区域那么肯定是有大小限制的,不可能无限大。
所以如果同时要执行的方法非常多的话(上面四种情况),每个方法对应虚拟机栈中的一个栈帧,肯定会挤爆这个区域。