我们知道在JVM内存模型中有个虚拟机栈的存在,它存在的目的是保存方法以及局部变量。
栈其原理是:它本身是一个栈的结构,那么栈的结构特点是,压栈和弹栈,当调用一个方法时,就创建一个该方法的栈帧,将其压入到虚拟机栈中,当该方法执行结束时,就将该栈帧弹出虚拟机栈。
按照这种原理,如果给定一个虚拟机的栈的深度,即栈的缓存容量,并且入栈和弹栈的效率假设是一样的话,那么就不存在神马StackOverFlowException异常了,那么到底是不是这样呢?我们用一个斐波那契数列测试一下:
package org.chenjianwen.test;
public class Fibonacci {
//F(0)=1,F(1)=1;当n>=2的时候,F(n)=F(n-1)+F(n-2)
//例如:F(2)=F(1)+F(0)=2;F(3)=F(2)+F(1)=3...
//F(0)~F(n)依次为:1,1,2,3,5,8,13,21...
public static int fibonacci(int n){
if(n < 0){
throw new RuntimeException("n不可为负数");
}
if(n == 0){
return 1;
}
if(n == 1){
return 1;
}
return fibonacci(n-1) + fibonacci(n-2);
}
public static void main(String[] args){
System.out.println(fibonacci(18));
}
}
当递归调用比较少的时候,例如10~20之间是可以运行的,但到50以上时,运行就卡壳了,当1000000时就会报出如下栈溢出异常。
由此可见,实际结果并不是像我们所想的那样,入栈和弹栈效率是一样的,那结果肯定是入栈的效率远远大于出栈的效率,导致栈帧的在虚拟机栈中的积压,从而导致栈帧数超出虚拟机的深度。
那么,有什么解决方法呢?两种,第一减少递归调用的次数,第二,如果不想减少递归调用次数,可以用循环代替,如下:
public static int[] circulationInvoke(int n) {
int[] arr = new int[n];
if (n == 1) {
arr[0] = 1;
}
if (n == 2) {
arr[0] = 1;
arr[1] = 1;
}
if(n > 2){
arr[0]=1;
arr[1]=1;
for (int i = 2; i < n; i++) {
arr[i] = arr[i - 1] + arr[i - 2];
}
}
return arr;
}
public static void main(String[] args) {
int[] arr = circulationInvoke(8);
for (int x : arr) {
System.out.println(x);
}
}
end。。。