スタックとヒープの違い:メモリ領域の使い分けを比較

動いているプログラムは、データのために、主に2つのメモリ領域を使います。スタックは、関数呼び出しのフレーム、つまりローカル変数、引数、戻りアドレスを保持します。自動で管理され、非常に速いですが、小さく、関数の呼び出しの寿命に縛られます。ヒープは、mallocやnewなどで、プログラムが明示的に要求したメモリを保持し、解放されるまで生き続けます。ヒープは柔軟で大きいですが遅く、メモリリークや断片化が起こるのもここです。

項目スタックヒープ
用途関数呼び出しのフレーム。ローカル変数、引数、戻りアドレス関数より長く生きる、またはサイズが事前にわからない、動的に確保するデータ
確保の方法自動。呼び出しと復帰のたびに、スタックポインタが動く明示的。mallocとfree、newとdelete、またはガベージコレクタ
速度非常に速い。ポインタを動かすだけ遅め。アロケータが空きのブロックを探し、記録を管理する
寿命関数が戻るまでメモリが解放される(またはガベージコレクションされる)まで
大きさ限りがある(通常はスレッドごとに数MB)使えるメモリの量だけが上限
配置通常は高いアドレスから下向きに伸びる通常は低いアドレスから上向きに伸びる。両者は互いに向かって伸びる
断片化なしありうる。確保と解放を繰り返すと、使えない隙間が残る
スレッドスレッドごとに専用のスタックがあるすべてのスレッドで共有するので、アロケータはスレッドセーフでなければならない
よくあるエラー深い再帰や大きなローカル配列によるスタックオーバーフローメモリリーク、解放後の使用、二重解放、メモリ不足

数値は代表的な目安で、世代や製品によって異なります。

どんなときに使うか

スタック
小さく短命で、サイズがコンパイル時にわかるデータ。ふつうのローカル変数など。
ヒープ
大きなデータ、サイズが実行時に変わるデータ、それを作った関数より長く生きるデータ。

よくある質問

なぜスタックの確保はヒープより速いのですか?

スタックの確保は、スタックポインタを動かすだけですが、ヒープの確保は、空きのブロックを探して、アロケータの記録を更新する必要があるからです。

スタックオーバーフローの原因は何ですか?

スタックを使いすぎることです。深い再帰や無限の再帰、非常に大きなローカル配列などで、スタックの大きさの上限を超え、プログラムが異常終了します。

メモリリークとは何ですか?

確保したのに解放されないヒープのメモリで、再利用できなくなります。リークが続くと、プログラムのメモリ使用量が増え続けます。

スレッドはスタックを共有しますか?

いいえ。スレッドごとに専用のスタックがあり、ヒープはすべてのスレッドで共有します。

理解度チェック

プロセスのヒープとスタックを、間に広い空きをはさんで反対向きに伸ばす配置にする利点として正しいものはどれか。

  1. Aどちらがどれだけ使うか前もって分からなくても、空きを両方で柔軟に分け合える
  2. Bヒープとスタックが同じ番地を同時に使えるので、メモリが2倍になる
  3. Cスタックがあふれることが絶対になくなる
答えと解説を見る

正解:A. どちらがどれだけ使うか前もって分からなくても、空きを両方で柔軟に分け合える

ヒープは番地の大きい方へ、スタックは小さい方へ伸びるので、間の空きは、ヒープを多く使うプログラムにもスタックを多く使うプログラムにも使えます。それぞれに固定の大きさを割り当てると、片方が余っていても他方があふれることがあります。ただしスタックには上限が設定されていることが多く、深すぎる再帰は上限を超えてスタックオーバーフローになります。

C言語のmallocで確保したメモリは、一般にプロセスのどの領域から取られるか。

  1. A.textセクション
  2. Bヒープ
  3. C.rodataセクション
答えと解説を見る

正解:B. ヒープ

ヒープは、プログラムの実行中に必要な大きさのメモリを確保し、不要になったらfreeで返す領域です。確保する大きさや寿命を実行時に決められるのが特徴です。mallocは自分で空き領域を管理し、足りなくなるとOSに頼んで領域を広げます(Linuxではbrkやmmapのシステムコール)。freeし忘れると、使わないメモリが残り続けるメモリリークになります。

Linuxなどの一般的なプロセスのメモリ配置を、番地の小さい方から並べたものとして正しいものはどれか。

  1. Aテキスト → データ → BSS → ヒープ → (空き) → スタック
  2. Bスタック → テキスト → ヒープ → データ → BSS
  3. Cヒープ → スタック → BSS → データ → テキスト
答えと解説を見る

正解:A. テキスト → データ → BSS → ヒープ → (空き) → スタック

典型的な配置では、番地の小さい側から機械語のテキスト、初期値のあるデータ、0で始まるBSSが並び、その上にヒープが番地の大きい方へ伸びます。スタックは番地の大きい側から小さい方へ伸び、両者の間の広い空き領域に共有ライブラリやmmapの領域が置かれます。実際の番地は、安全のためASLRによって実行のたびにずらされます。

  1. 再帰が深くなりすぎてスタックの上限を超えたとき、Linuxなどで一般に起こることとして正しいものはどれか。
  2. mallocとfreeを繰り返すうちに、空きメモリの合計は十分あるのに、大きな領域をまとめて確保できなくなることがある。この現象を何と呼ぶか。
  3. RISC-Vで、自分自身を呼び出す再帰関数を書くときに、raをスタックに保存し忘れるとどうなるか。

リンカ・ローダと実行形式の練習問題へ

関連する用語の解説

スタックフレーム
関数が呼ばれるたびにスタック上に確保する領域。戻り先、保存したレジスタ、局所変数などを置き、関数から戻るときに解放する。
スタックポインタ
スタックの今の先頭の番地を指すレジスタ。RISC-Vではsp(x2)。多くのISAでスタックは番地の小さい方へ伸びる。
ヒープ
プログラムの実行中にmallocなどで必要な大きさのメモリを確保し、freeで返す領域。確保と解放を繰り返すと断片化が起きることがある。

関連する比較