2016-05-15 7 views
3

更新:コールスタックは、タイプTとは独立して成長するので、この質問はあまり意味がないことに注意してください。コールスタックは、処理されるリスト内のノードの数にのみ依存しますコールスタックのサイズに影響を与えません。どのくらいのスタックで再帰が使用されますか(リストからノードを削除する)?


のは、私は次のようになります「削除-ノードからリスト」方式の再帰的なJava実装を持っているとしましょう:

// head 
public void remove(T data) { 
    if (data == null) 
     throw new IllegalArgumentException(); 
    head = remove(head, data); 
} 

// other nodes of list 
private ListNode remove(ListNode current, T data) { 
    if (current == null) 
     return null; 
    if (current.data.compareTo(data) == 0) 
     return current.next; 
    current.next = remove(current.next, data); 
    return current; 
} 

レッツ・さらにタイプTint

であることを前提としてい

私の質問は次のとおりです:処理中のリストのサイズと比較してコールスタックが(最悪の場合)どのくらいの大きさになるのですか? 私は、Java 8は、再帰を最適化していない見てきたものから(右私の全体の分析は、これが真実であることを前提として?):私はこれまで考えた何

。したがって、リストのサイズで直線的に増加するコールスタックがあります。

もっと正確には、このメソッドremove(ListNode current, T data)は基本的に、リスト内の各ノードの呼び出しスタック上に1つのノード参照(32ビットアーチで32ビット)とint(32ビット)を配置します。

ここで、リスト内のノードも1つの参照と1つのintで構成されているので、ノードは実際にはそれらの再帰呼び出しの1つとほとんど同じサイズです。実際には、これらの呼び出しのうちの1つがさらにスペースを使用することもあります(戻りアドレスもスタックにプッシュする必要がありますか、それともどうにかして最適化できますか?)。

もし私が正しいならば、呼び出しスタックは基本的に最悪の場合(削除される要素がリストの最後にあるとき)、リスト自体と同じ大きさになります!これは事実ですか?私が正しいとすれば、intのリストを処理するために必要なコールスタックに関して、これは1倍またはそれ以上の大きさを意味します。結果として、大きなリスト(実際にはそれほど大きくない)を処理すると、1Mと言うと、stacksize(0.5M)のデフォルト設定で実行するとスタックオーバーフローが発生します。

私は、より大きいデータ型の場合、係数は小さくなりますが、どんな場合でも私がしっかりと立つことは基本的には次のようになります。最適化が行われない場合、コールスタックはリストのサイズに従って線形に増加します再帰的な手順よりも反復的な方が望ましい。

一般に、コールスタックは、のリストの場合はほぼ同じですが、(size_of_all_arguments_in_call + size_of_return_address)/size_of_a_single_listelementファクタで線形に大きくなると思いますか?私はsize_of_all_argumentsで間違ってはいけません。私は、それらの呼び出し内のオブジェクトへの参照の値だけを渡し、オブジェクト全体ではないことを知っています。したがって、大きなオブジェクトの場合、その要素はそれほど劇的ではないかもしれませんが、私はまだ、その不要な線形スペースオーバヘッドを受け入れるべきではないと言います。しかし、intのリストについては、因子は約であろう。 1.

この解析は正しいですか、何か不足していますか?

背景:私はこれを議論して人は、Javaでコールスタックはそれほど劇的に成長しないことを私を説得しようとしていると私は何かを明らかに行方不明になった指摘された(残念ながら私に言うことができなかったものを、私にそれ本当に)は全く明らかにされていません - 私はそれは私が学ぶこと熱望していないのですが何であるかを知らないからである。)

関連:

私のための関連質問に欠けている主なものは、コールスタックが実際にからノードを削除する(スタックは再帰を使用しないどのくらい

+0

JVMが何かを考慮しているかどうかはわかりませんが、この投稿とリンクされた複製が表示されます。http://stackoverflow.com/questions/17250883/is-it-possible-to-determine-how-much -space-available-on-the-stack –

+0

Javaの質問が見つかりましたhttp://stackoverflow.com/questions/20030120/java-default-stack-size –

+0

私はあなたもこれについてフレームサイズを考慮する必要があると思います: http://www.herongyang.com/JVM/Stack-Overflow-and-JVM-Stack-Frame.html – Lee

答えて

0

をどのくらいのスペースを使用しますですリスト)?

コードをプロファイリングしたり、特定のJVMを特定したりすることなく答えるのは難しい質問です。これは、Java Virtual Machine Specification (JVMS)が実装者に多くの詳細を残しているからです。

JVMS Chapter 2: Java仮想マシンの仕様の一部ではありません実装の詳細は不必要に実装の 創造性を制約するでしょう。たとえば、ランタイム データ領域のメモリレイアウト、使用されるガベージコレクションアルゴリズム、およびJava仮想マシン命令の内部 (たとえば、機械コードに変換する )は、 の実装者。

これらの詳細の一部は、スタックおよびスタックフレームの実装です。再帰的な削除によって作成されたスタックとアルゴリズムが処理しているリストとの関係について実際に尋ねているようだから、スタックフレームはあなたが思っているほど大きくないかもしれないと考える必要があります(またはあなたの質問に提案する)。 Java仮想マシンのスタックプッシュとポップするフレームを除いて直接操作 は決してありませんので

JVMS (§2.5.2):は、フレームが ヒープが割り当てられてもよいです。

つまり、スタックフレームごとに1つの参照をスタックに置くことができます。そうであれば、それはスタックとそれが処理しているリストとの間の関係を、あなたが提供している例にとって本当に重要でないものにします。

よるAlgorithms (Sedgewick, Wayne)へ:

  • Integerは24バイト
    オーバーヘッド+インスタンス変数(int)+パディング
    = 16 + 4 + 4 = 24バイトであるのは、いくつかの数字を見てみましょう

  • 非静的に再帰的に定義されたNodeのクラスは、関連付けられた Listしたがって、我々は内のエントリごとに64のバイトがあるか見ることができる40バイト オーバーヘッド+参照+追加のオーバーヘッド(Listクラスを参照)
    = 16 + 8 + 8 + 8 = 40バイト

ありますリスト。したがって、フレームがヒープに割り当てられている実装がある場合、スタックと処理しているリストとの関係は8/64 = 1/8 bytesになります。

スタックフレームがスタックに割り当てられている場合、スタックのサイズを特定することはより困難です。実際には、実装の詳細を判断する必要があります。

JVMS (§2.6. Frames): ローカル変数配列とオペランドスタックの大きさは、コンパイル時に 決定され、フレーム(§4.7.3)に関連 メソッドのコードと一緒に供給されます。したがって、 フレームデータ構造のサイズは、Java 仮想マシンの実装にのみ依存し、これらの構造のメモリはメソッド呼び出し時に に同時に割り当てることができます。

たとえあなたの例では、スタックがリストに関して1:1または1:1近くに成長する可能性は低いと思います(ここではメモリの合計サイズについて説明しています)。確かに、あなたの議論を検証する特定の問題を提示することはできますが、それはあなたが提供した例ではない可能性があります。

関連する問題