web-dev-qa-db-ja.com

メモリがスタックとヒープに分割されるのはなぜですか?

重複の可能性:
スタックとヒープはどこにありますか

スタックとヒープについていくつか質問があります。

知っておくべき基本的なことは、スタックはヒープよりも高速ですが、制限があるということです。 (私が間違っている場合は私を訂正してください)。

しかし、私はいつもスタックとヒープが正確にどのように機能するのか疑問に思いました。 RAMはメモリの1つのチャンクであり、「スタック」と「ヒープ」に分割されていません(またはそうですか?)。そうであれば、なぜそもそもメモリをスタックとヒープに分割しますか?

OSを使用すると、スタック上のすべてを割り当てることができます->すべてが速くなります->幸せな世界ですか?

そうではないと確信しています。しかし、なぜ!?誰かが私に詳細な答えを与えることができますか?

申し訳ありませんが、この投稿が誰かによって作成された投稿の複製である場合、スタックとヒープに関連するものが非常に多いため、正確な質問を見つけることができませんでした。知っている場合は、リンクしてください。

34
xcrypt

Stack:スタックは、現在実行中のコードのブロック、現在のブロックと呼ばれるブロック、そのブロックと呼ばれるブロックなどで使用される一時的なスクラッチパッドの一種として使用されます。現在のブロックが終了すると、それが使用していたローカル変数は忘れられます。名前が示すように、スタックはラストイン、ファーストアウトの方法で使用されます。

スタックの最も重要な用途の1つは、現在のコールチェーンを追跡することです。ある関数が別の関数を呼び出すと、呼び出し元は次の命令のアドレス(リターンアドレス)をスタックにプッシュします。各関数が終了すると、呼び出し元の戻りアドレスがスタックからポップされ、そのアドレスから始まるコードの実行が続行されます。また、呼び出し元と呼び出し先の間で関数パラメーターと戻り値を通信するためにも使用されます。

ヒープ:ヒープは異なります-特定の順序はありません。コードのブロックにメモリを割り当て、そのメモリをブロックの終わりを超えて固定する場合は、ヒープに割り当てます。もちろん、他のコードがそのメモリを見つけられるように、ポインタ/参照をどこかに格納する必要もあります。ほとんどの言語はその宿泊施設を提供します。

速度:速度の違いは、メモリ自体のプロパティによるものではありません。質問でおっしゃるように、通常、スタックとヒープの両方が同じ物理メモリに存在します。スタックのおかげで、スタックにスペースを割り当てるのは簡単ですLIFO性質:スタックに何かをプッシュすると、最終的には1つの場所しかありません。対照的に、ヒープにブロックを割り当てるには、メモリ内で十分な大きさの連続した空き領域を見つける。スタック割り当ては単一の命令と同じくらい速くなる可能性があります。ヒープ割り当てには、malloc()のようなメモリ割り当て関数の呼び出しが必要です。

静的v。動的:ヒープへのメモリの割り当ては動的です-ブロックを割り当てるかどうか、およびブロックのサイズは、プログラムの実行中に受け取る入力に応じて決定できます。ヒープに割り当てられたメモリの領域は、必要に応じてサイズを変更することもできます。スタックにメモリを動的に割り当てることも可能です(C標準ライブラリ関数alloca()を参照)が、そのメモリは現在の関数が終了するとすぐに失われます。スタックの割り当ては通常静的です。コンパイラは、(非レジスタ)パラメータ、戻りデータ、およびローカル変数に必要なスペースを決定し、関数が呼び出されたときにスタックに必要なスペースを予約するコードを生成します。

例:ワードプロセッサを作成していると想像してください。ドキュメントの大きさや、同時に使用されるドキュメントの数を事前に知ることはできません。同時に、ユーザーが開いたままにしておきたい限り、ユーザーのドキュメントをメモリに残しておく必要があります。スタック上のドキュメントにメモリを割り当てようとすると、一度に複数のドキュメントを開くことが難しくなり、ドキュメントを作成、編集、保存、および閉じる単一の関数を作成する必要があります。ヒープにスペースを割り当てると、必要な数のドキュメントを作成でき、それぞれに含まれるデータに適したサイズになり、ドキュメントの有効期間が特定の関数の有効期間に関連付けられるのを防ぐことができます。

要約:簡単に言うと、スタックは変数の値を保持し(代わりにレジスタが使用されることもあります)、ヒープは現在のブロックの有効期間を超えて使用されるメモリを割り当てるために使用されます。

35
Caleb

スタックには後入れ先出しの割り当てと割り当て解除の順序が必要なため、スタックのみを使用することはできません(つまり、割り当てを解除できるのは最新のデータのみです。スタックでは、古いデータの割り当てを解除して新しいデータを保持することはできません)。

実際には、スタックを取り除くことができます(ヒープを保持するだけです)。 Appelの論文 ガベージコレクションはスタック割り当てよりも高速である可能性があります および彼の 継続でコンパイル 本を参照してください。

また、ヒープには明確に定義された意味がありません(「スタック上にない動的に割り当てられたメモリ」以外)。実際、Linuxシステムでは、 mmap システムコールを使用してメモリの大きなチャンクを割り当てるのはかなり高速です(ただし、malloc実装はmmapを避け、free- dメモリを再利用することを好みます)。問題は、小さなメモリゾーンの割り当てです。

そして、 ガベージコレクションテクニック についてもっと読んでください。 CまたはC++では、 Boehm's GC を使用できます。

スタックは、特に再帰的な関数呼び出しに役立つことがよくあります。これは非常に便利であるため(Cなど)、今日のプロセッサには通常、専用のスタックポインタレジスタがあります(呼び出しと戻りのためにCALLとRETのマシン命令で使用されます)。しかし、これは常に当てはまるわけではありません。一部のプロセッサー(IBM360など)では、スタックポインターは従来のレジスターであり、ハードコードされたレジスターではありません。

thisthat 回答(およびその他の回答) 仮想アドレス空間 も参照してください。

10

メモリはどちらも同じですが、スタックとヒープは2つの異なるデータ構造であり、さまざまな目的に役立ちます。

スタックは非常に原始的な抽象化であり、いくつかのオペランド(通常はプロセッサレジスタまたはメモリアドレス)で命令を実行するためにマイクロプロセッサが必要とします。

ヒープは、通常、スタックにバインドされていないデータを格納する一般的な割り当てメモリ領域です。つまり、スタックに格納されている場合よりも寿命が長くなります。言い換えると、データは別の方法でアクセスされます。コードの一部。

0