多少个函数调用将导致堆栈溢出

时间:2012-09-01 18:55:20

标签: java android exception-handling error-handling

你好android / Java开发者,

当一个函数调用一个函数并且该函数调用另一个函数时,有多少调用(堆栈长度)会让我进入堆栈流量?有一般的经验法则吗?

我问的原因是因为我现在对于我的5个玩家纸牌游戏来说效率更高(设计明智)

解决方案1:

for(int i=0;i<100;i++){
         p1.play();
         p2.play();
         p3.play();
         p4.play();
}

解决方案2:

   p1.play();    //where p1.play() calls p2.play() and so on until p4 calls p1 again.   
                 // this will go on for 100 times

我更喜欢解决方案2所以如果发生崩溃,我可以看到从i = 0的p1到i = 100的p4的所有函数调用

但是对于解决方案1,堆栈要短得多,但是当发生崩溃时,我会在循环的开头看到一个被调用的函数play(),其中发生了崩溃

你有什么建议?我知道它有点2个问题,但它们非常相关

谢谢大家

6 个答案:

答案 0 :(得分:3)

根据我的经验,Java中的堆栈溢出几乎总是由编程错误引起的。查看典型尺寸here

现在,你的第二个解决方案是,IMO,非常难看......几乎是编程错误。假设N = 100是(有点)游戏的持续时间,那么内存消耗(堆栈大小)随之增加,这听起来是错误的。我根本不喜欢这个解决方案。

  

当发生崩溃时,我会在循环的开头看到一个   调用函数play()发生崩溃

我没有看到真正的优势。为什么不放一个try catch块,以便在发生崩溃时可以打印出迭代编号?

答案 1 :(得分:2)

创建堆栈溢出的递归或嵌套函数没有一般的经验法则。相反,它取决于堆栈上可用的内存,这可能会因底层硬件和操作系统分配而异。

如果没有看到更多代码,很难确定哪种函数调用方法更适合您的情况。我会支持前(第一个)选项,因为它对正在发生的事情更加明确,并且它避免将可能不一定相互依赖的可能方法和对象实例链接在一起。如果您主要关注错误报告,可以尝试将日志添加到代码中,以便更详细地了解正在发生的事情,同时查看堆栈跟踪的转储。希望此链接也可以为您提供帮助:http://developer.android.com/tools/debugging/index.html

答案 2 :(得分:2)

我认为Shivan Dragon是对的,没有固定数量的通话,这会导致溢出。但是你可以用一个非常简单的递归函数来测试它:

public void stackTest(int iteration)
{
    System.out.println("Iteration: "+iteration); // or Log
    stackTest(iteration+1);
}

并称之为:

stackTest(1);

然后看看它走了多远。

答案 3 :(得分:1)

我认为你不能说一般数量的x函数调用会触发堆栈内存的溢出。它取决于函数,它们的参数,它们的返回类型等,都保存在堆栈内存中,因此不同的函数可能会占用不同数量的(堆栈)内存。

无论如何,你不应该依赖于远远接近崩溃堆栈的代码,试图考虑使用了多少堆栈。你的代码总是可以避免溢出堆栈。

答案 4 :(得分:0)

Java中的每个方法frame都有:局部变量表和操作数堆栈。此堆栈大小是常量,每个方法可能具有不同的大小。由于JVM规范,操作数堆栈大小存储在Code字段中的方法属性max_stack中:

Code_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 max_stack;
    u2 max_locals;
    u4 code_length;
    u1 code[code_length];
    u2 exception_table_length;
    {   u2 start_pc;
        u2 end_pc;
        u2 handler_pc;
        u2 catch_type;
    } exception_table[exception_table_length];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

此大小在编译期间计算,当您点击StackOverflowException时可能会被JVM更改。 JVM规范:

  

以下异常情况与Java虚拟机堆栈相关联:如果线程中的计算需要比允许的更大的Java虚拟机堆栈,则Java虚拟机会抛出StackOverflowError。如果可以动态扩展Java虚拟机堆栈,并且尝试进行扩展但是可以使内存不足以实现扩展,或者如果可用的内存不足以为新线程创建初始Java虚拟机堆栈,则Java虚拟机机器抛出OutOfMemoryError。

总结:它取决于您的JVM允许获取多少内存。在不同的工作站/智能手机上,您可能拥有不同的可用内存值。这就是为什么你不应该编写依赖于这些东西的代码。如果您认为可能发生OutOfMemoryException,请尝试迭代解决问题,而不是递归。

答案 5 :(得分:0)

这归结为递归与迭代的原理。

递归允许您以简单的方式编写算法;实施起来更容易理解和更快捷。 递归算法可以转换为迭代算法,这将有更高的内存效率和更高的CPU效率。

因此决定是性能与简单/编码工作之一。

以下是关于此主题的主题: Recursion or Iteration?