在java语言中,使用递归调用时,如果过多的调用容易造成java.lang.StackOverflowError即栈溢出和程序执行过慢。这是一个潜在Bug和影响程序执行效率问题,需要谨慎使用。
 
下面先看造成java.lang.StackOverflowError即栈溢出问题:
public class RecursionTest
{ public static void recursion(int totalTimes,int time)
{
if(totalTimes > 1)
{
System.out.println("这是第 " + time + "次调用!");
totalTimes--;
time++;
recursion(totalTimes, time);
}
else
{
System.out.println("调用结束,共调用了" + time + "次");
return;
}
} public static void main(String[] args)
{
int totalTimes = 1000000;
int time = 1;
long startTime = System.currentTimeMillis();
System.out.println("嵌套调用起始时间:" + startTime);
recursion(totalTimes, time);
System.out.println("嵌套调用结束时间:" + System.currentTimeMillis());
System.out.println("总耗时:" + (System.currentTimeMillis() - startTime));
System.out.println("------------------------------------------------------------");
} }

修改“totalTimes”,当到达一定值时,报如下错误:

在开发时,要注意避免该问题,特别是递归过多调用时,最好改为for或者whlie来代替。
如下cycle()方法:
	public static void cycle(int totalTimes, int time)
{
if(totalTimes > 1)
{
System.out.println("这是第 " + time + "次调用!");
}
else
{
System.out.println("调用结束,共调用了" + time + "次");
}
}

代替recuresion()方法:

public static void recursion(int totalTimes,int time)
{
if(totalTimes > 1)
{
System.out.println("这是第 " + time + "次调用!");
totalTimes--;
time++;
recursion(totalTimes, time);
}
else
{
System.out.println("调用结束,共调用了" + time + "次");
return;
}
}

再看一下对程序执行效率的影响:
仍然使用上面的例子,分别cycle()方法和在main()方法中加入如下代码:
		startTime = System.currentTimeMillis();
System.out.println("循环调用起始时间:" + startTime);
for (int index = totalTimes; index > 0; index--)
{
cycle(index, time);
time++;
}
System.out.println("循环调用结束时间:" + System.currentTimeMillis());
System.out.println("总耗时:" + (System.currentTimeMillis() - startTime));
System.out.println("--------------------

整个代码如下:

public class RecursionTest
{ public static void recursion(int totalTimes,int time)
{
if(totalTimes > 1)
{
System.out.println("这是第 " + time + "次调用!");
totalTimes--;
time++;
recursion(totalTimes, time);
}
else
{
System.out.println("调用结束,共调用了" + time + "次");
return;
}
} public static void cycle(int totalTimes, int time)
{
if(totalTimes > 1)
{
System.out.println("这是第 " + time + "次调用!");
}
else
{
System.out.println("调用结束,共调用了" + time + "次");
}
} public static void main(String[] args)
{
int totalTimes = 100000;
int time = 1;
long startTime = System.currentTimeMillis();
System.out.println("嵌套调用起始时间:" + startTime);
recursion(totalTimes, time);
System.out.println("嵌套调用结束时间:" + System.currentTimeMillis());
System.out.println("总耗时:" + (System.currentTimeMillis() - startTime));
System.out.println("------------------------------------------------------------"); startTime = System.currentTimeMillis();
System.out.println("循环调用起始时间:" + startTime);
for (int index = totalTimes; index > 0; index--)
{
cycle(index, time);
time++;
}
System.out.println("循环调用结束时间:" + System.currentTimeMillis());
System.out.println("总耗时:" + (System.currentTimeMillis() - startTime));
System.out.println("------------------------------------------------------------");
} }
 
分别测试totalTimes为500,1000,3000对比结果:
 
500次:
1000次:


 
3000次:
 
以下是从网络上摘抄的:
根本原因是这样的,对于每一个线程,都有一个java栈 ,当有一个方法被调用的时候,会产生一些跟这个方法相关的信息,如方法名,参数,中间变量等等,这些叫做栈帧 ,当一个方法执行完毕  这个栈帧才会从栈顶pop掉  你递归的话  会一直向栈里push栈帧  而这个java栈是有一定的长度或深度的,当栈满了,无法再进行push的时候 就出现你上面的异常了,解决办法的话 就不要用递归操作 改用for 而且平时也不建议用递归的,效率太低了 .

栈溢出了,JVM依然是采用栈式的虚拟机,这个和C和Pascal都是一样的。函数的调用过程都体现在堆栈和退栈上了。你调用构造函数的“层”太多了,以致于把栈区溢出了。  
通常来讲,一般栈区远远小于堆区的,因为函数调用过程往往不会多于上千层,而即便每个函数调用需要1K的空间(这个大约相当于在一个C函数内声明了256个int类型的变量),那么栈区也不过是需要1MB的空间。通常栈的大小是1-2MB的。通常递归也不要递归的层次过多,很容易溢出.  

 
对java.lang.StackOverflowError的分析:  
原因:运行一个程序,JVM会开辟一块内存空间去储存程序进行时的某些信息,当程序运行时需要储存的信息超过了分配的空间,就会出现那样的问题.比如死循环,  
解决:首先从程序代码优化方面着手,检查是否有死循环、递归等程序,如果有,修正、优化相关代码。  

Java递归调用改成非递归的更多相关文章

  1. 深度优先搜索(DFS)递归形式改为非递归形式

    DFS将递归改为非递归这个方法的需求来自于一道三维积木组合的题目,还在苦苦调试中,暂且不提. 普通的认识对于递归向非递归的转化无非是使用栈,但是结合到深度搜索如何将栈很好利用,如何很好保存现场,都不是 ...

  2. C# Windows Phone 8 WP8 开发,将WebClient的DownloadStringCompleted事件改成非同步的awiat方法。

    原文:C# Windows Phone 8 WP8 开发,将WebClient的DownloadStringCompleted事件改成非同步的awiat方法. 一般我们在撰写Windows Phone ...

  3. Java 递归、尾递归、非递归、栈 处理 三角数问题

    import java.io.BufferedReader; import java.io.InputStreamReader; //1,3,6,10,15...n 三角数 /* * # 1 * ## ...

  4. Java 递归、尾递归、非递归 处理阶乘问题

    n!=n*(n-1)! import java.io.BufferedReader; import java.io.InputStreamReader; /** * n的阶乘,即n! (n*(n-1) ...

  5. java创建二叉树并实现非递归中序遍历二叉树

    java创建二叉树并递归遍历二叉树前面已有讲解:http://www.cnblogs.com/lixiaolun/p/4658659.html. 在此基础上添加了非递归中序遍历二叉树: 二叉树类的代码 ...

  6. java只http改成https访问

    目录 生成keystore文件 修改tomcat中的server.xml文件 配置浏览器 生成keystore文件: 1.在tomcat的bin 目录下输入命令:keytool -genkeypair ...

  7. 如何由jdk的安装版本改成非安装版本

    背景. 官网一般只提供windows下的exe文件,不提供zip打包文件.有些不愿意使用安装版本. 解决方法 本文以windows 7下安装jdk-6u35-windows-x64.exe为例说明 1 ...

  8. JAVA 遍历文件夹下的所有文件(递归调用和非递归调用)

    JAVA 遍历文件夹下的所有文件(递归调用和非递归调用) 1.不使用递归的方法调用. public void traverseFolder1(String path) { int fileNum = ...

  9. [复习] JAVA 遍历目录 (递归调用和非递归)

    JAVA 遍历文件夹下的所有文件(递归调用和非递归调用) 1.不使用递归的方法调用. public void traverseFolder1(String path) { int fileNum = ...

随机推荐

  1. html5--6-57 阶段练习6-折叠导航栏

    html5--6-57 阶段练习6-折叠导航栏 实例 @charset="UTF-8"; *{ ; ; } h3+div{ ; overflow: hidden; transiti ...

  2. Tomcat 系统架构与设计模式之二

    Tomcat 系统架构与设计模式,第 2 部分: 设计模式分析 来自:http://www.ibm.com/developerworks/cn/java/j-lo-tomcat2/ 这个分为两个部分的 ...

  3. 小K的农场(差分约束)

    题目大意 n个点 m条描述 农场 a 比农场 b 至少多种植了 c 个单位的作物. 农场 a 比农场 b 至多多种植了 c 个单位的作物. 农场 a 与农场 b 种植的作物数一样多. 题解 差分约束裸 ...

  4. delphi 2010 启动卡死,过一段时间后出现“displayNotification:堆栈溢出 怎么解决?

    解决方法:打开IE,在Internet选项中,删除历史记录.

  5. 【198】Synergy - 鼠标键盘共享软件

    参考:Synergy X64 v1.7.4 官方最新版 参考:Synergy安装方法 功能介绍: 可以将配置局域网的电脑实现同一个鼠标键盘控制两台电脑,效果类似一台电脑使用双屏的效果,键盘会根据鼠标的 ...

  6. Codechef QGRID

    QGRID code 给定一个 n × m(1 <= m <= 3) 的点网格,网格的边上以及点上都有权值.初始时所有点的权值都为 0 .维护两种操作:1. x1 y1 x2 y2 c 把 ...

  7. ibatis 中 $与#的区别

    ibatis 中 $与#的区别 使用#: select * from table where id = #id# 如果字段为整型:#id#表示成id select * from table where ...

  8. grep在指定类型的文件中查找字符 (转载)

    转自:http://blog.csdn.net/qvbfndcwy/article/details/8127329 find -name '*.php'|xargs grep 'include'//在 ...

  9. emacs配置文件的基础知识 (转载)

    转自:http://blog.csdn.net/schumyxp/article/details/2278268 emacs的配置文件,叫作.emacs,是个隐藏文件,存在于当前用户的根目录下面,也就 ...

  10. Spring-SpringMVC父子容器&AOP使用总结

    此文已由作者尧飘然授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. Spring&SpringMVC Spring&SpringMVC作为bean管理容器和MV ...