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() がクラッシュが発生した場所で表示されます。

何を指示してるんですか?2つの質問を1つにまとめたようなものですが、それらは非常に関連しています

皆さん、ありがとうございました

4

6 に答える 6

3

私の経験では、Java でのスタック オーバーフローはほとんどの場合、プログラミング エラーが原因です。一般的なサイズはこちらをご覧ください。

さて、あなたの2番目の解決策は、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];
}

このサイズはコンパイル中に計算され、ヒットしたときに JVM によって変更される可能性がありますStackOverflowException。JVM 仕様:

Java 仮想マシン スタックには、次の例外的な条件が関連付けられています。Java 仮想マシン スタックが動的に拡張可能であり、拡張が試行されたが、拡張を実行するために使用できるメモリが不足している場合、または新しいスレッド用の初期 Java 仮想マシン スタックを作成するために使用できるメモリが不足している場合、Java 仮想マシンが OutOfMemoryError をスローします。

要約すると、JVM が取得できるメモリの量によって異なります。異なるワークステーション/スマートフォンでは、使用可能なメモリの値が異なる場合があります。これが、そのようなものに依存するコードを書くべきではない理由です。それが発生する可能性があると思われる場合はOutOfMemoryException、再帰的ではなく反復的に問題を解決してみてください。

于 2012-09-01T19:10:07.917 に答える
0

これは、再帰と反復の原則に帰着します。

再帰を使用すると、簡単な方法でアルゴリズムを作成できます。より理解しやすく、より迅速に実装できます。再帰アルゴリズムは、メモリ効率と CPU 効率が向上する反復アルゴリズムに変換できます。

したがって、決定は、パフォーマンスとシンプルさ/コーディングの労力のいずれかです。

このトピックに関するスレッドは次のとおりです: Recursion or Iteration?

于 2012-09-01T19:26:48.943 に答える