6

您好 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() 发生崩溃的地方

你有什么建议?我知道这是 1 中的 2 个问题,但它们非常相关

谢谢你们

4

6 回答 6

3

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

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

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

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

于 2012-09-01T19:12:18.170 回答
2

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

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

于 2012-09-01T19:05:29.750 回答
2

我认为 Shivan Dragon 是对的,没有固定数量的调用,这会导致溢出。但是,您可以使用非常简单的递归函数对其进行测试:

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

并称它为:

stackTest(1);

然后看看能走多远。

于 2012-09-01T19:08:13.737 回答
1

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

无论如何,您永远不应该依赖那些通过尝试考虑使用了多少堆栈而接近崩溃堆栈的代码。您的代码应该始终避免溢出堆栈。

于 2012-09-01T19:01:11.160 回答
0

Java中每个方法的框架都有:局部变量表和操作数栈。这个堆栈大小是恒定的,每个方法可能有不同的大小。由于 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规范:

以下异常情况与 Java 虚拟机堆栈相关: 如果线程中的计算需要的 Java 虚拟机堆栈超出允许范围,则 Java 虚拟机将引发 StackOverflowError。如果 Java 虚拟机堆栈可以动态扩展,并且尝试进行扩展,但没有足够的内存来实现扩展,或者如果没有足够的内存来为新线程创建初始 Java 虚拟机堆栈,则 Java 虚拟机器抛出 OutOfMemoryError。

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

于 2012-09-01T19:10:07.917 回答
0

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

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

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

这是关于此主题的主题: 递归还是迭代?

于 2012-09-01T19:26:48.943 回答