TL

メモリ管理(スタックとヒープ)

プログラムが使うメモリは「スタック」と「ヒープ」に大きく分かれる。自動で片付く速い領域と、自分で確保して管理する自由な領域。その違いを押さえると、メモリリークやスタックオーバーフローの正体が見えてくる。

中級メモリ管理スタックヒーププロセス最終更新: 2026-06-04
TL;DR要点だけ先に
  • 1.プロセスのメモリは コード/データ/ヒープ/スタック の4領域。可変なのはヒープとスタック。
  • 2.スタックは 自動・LIFO・高速:関数呼び出しごとに積み下げされ、関数を抜けると自動で解放。
  • 3.ヒープは 動的確保・手動 or GC:寿命を自分で管理する代わりに自由。放置するとメモリリークになる。

プロセスのメモリ構成

OS はプロセスを起動するとき、そのプロセス専用のメモリ空間(アドレス空間)を割り当てます。中身はおおまかに4つの領域に分かれます。

高位アドレス
┌──────────────┐
│   スタック    │  ← 関数呼び出し・ローカル変数(下に伸びる ↓)
├──────────────┤
│      ↓        │
│   (空き)    │
│      ↑        │
├──────────────┤
│   ヒープ      │  ← 動的確保したデータ(上に伸びる ↑)
├──────────────┤
│  データ領域   │  ← グローバル変数・静的変数(.data / .bss)
├──────────────┤
│  コード領域   │  ← 機械語の命令(.text、通常は読み取り専用)
└──────────────┘
低位アドレス
  • コード領域(text):プログラムの機械語命令。実行中に書き換わらないので読み取り専用。
  • データ領域(data / bss):グローバル変数や静的変数。プロセスの寿命のあいだずっと存在する。
  • ヒープ:実行中に動的に確保するメモリ。伸び縮みする。
  • スタック:関数呼び出しとローカル変数のための領域。これも伸び縮みする。

サイズが固定されないのは ヒープとスタック の2つです。図のように両者は空き領域を挟んで向かい合わせに配置され、片方が伸びすぎてもう片方とぶつかると破綻します(後述)。

“スタック”という言葉は2つある

データ構造としての スタック(LIFO のコレクション) と、ここで言う コールスタック(関数呼び出しのためのメモリ領域) は別物です。ただしコールスタックが LIFO で動くのは、まさにスタックというデータ構造そのものだから。名前が同じなのは偶然ではありません。

スタック:自動・LIFO・高速

スタックは 関数呼び出し のための領域です。関数を呼ぶたびに スタックフレーム(活動レコード) が1つ積まれ、そこに次のものが入ります。

  • 関数の ローカル変数
  • 関数の 引数
  • 戻り先アドレス(呼び出し元に帰る場所)

そして関数を抜けると、そのフレームは まるごと捨てられます。最後に積んだものが最初に外れる LIFO(Last In, First Out) なので、解放はスタックポインタを動かすだけ。だから 非常に高速 で、プログラマが解放を意識する必要がありません

function a() が b() を呼び、b() が c() を呼ぶと…

呼び出し前   a 実行中    b 実行中    c 実行中    c 終了後
                                  ┌─────┐
                        ┌─────┐  │  c  │   ┌─────┐
              ┌─────┐  │  b  │  ├─────┤   │  b  │
   (空)       │  a  │  │  a  │  │  a  │   │  a  │
              └─────┘  └─────┘  └─────┘   └─────┘

その代わり、スタックに置けるのは基本的に コンパイル時にサイズが決まるデータ や、その関数の中だけで使う一時的なデータ です。寿命は「関数の実行中」に縛られます。

ヒープ:動的確保・手動 or GC

ヒープは 実行時に、必要なサイズを必要なときに確保する ための領域です。スタックと違い、確保したメモリは関数を抜けても生き続けます。だからこそ「関数をまたいで共有するデータ」「実行するまでサイズが分からないデータ」に向きます。

代わりに、いつ解放するかを誰かが管理しなければなりません。方式は大きく2つです。

  • 手動管理:C/C++ など。malloc/freenew/delete で自分で確保・解放する。自由だが、解放忘れ=メモリリーク、二重解放=バグの温床。
  • 自動管理(GC):Java・C#・Go・JavaScript・Python など。ガベージコレクタ が「もう誰からも参照されていないオブジェクト」を見つけて自動回収する。楽だが、回収のタイミングや一時停止(GC ポーズ)は制御しにくい。
// C:手動管理の例
int *p = malloc(sizeof(int) * 100); // ヒープに確保
// ... p を使う ...
free(p);   // 自分で解放する。忘れるとリーク
p = NULL;  // 解放後に触らないよう無効化
GC があってもリークは起きる

「GC 言語ならメモリリークは無縁」は誤解です。GC は “到達不能”になったものだけ を回収します。グローバルな配列やキャッシュ、解除し忘れたイベントリスナなどから参照が残り続けると、不要なオブジェクトでも 回収されず溜まり続けます。これも立派なメモリリークです。

スタック vs ヒープ

観点スタックヒープ
確保のされ方関数呼び出しで自動実行時に明示的に要求
解放関数を抜けると自動手動 or GC が回収
寿命その関数の実行中だけ解放/回収されるまで(関数をまたげる)
速度非常に速い(ポインタ移動のみ)比較的遅い(空き管理が必要)
サイズ小さめ・上限あり大きく確保できる
主な失敗スタックオーバーフローメモリリーク・断片化
どちらに置かれるかは言語しだい

「ローカル変数=スタック、new したもの=ヒープ」が基本ですが、絶対ではありません。Java では基本型のローカル変数はスタック、オブジェクト本体はヒープ(変数は参照を持つ)。Go ではコンパイラの エスケープ解析 が「関数の外へ漏れるか」を判断し、漏れる値だけヒープに回します。JavaScript はプリミティブ以外を実質ヒープで扱います。配置は言語・処理系が決めるもの、と覚えておくと混乱しません。

つまずきポイント①:スタックオーバーフロー

スタックには 上限サイズ があります(OS やスレッド設定で決まり、数 MB 程度が一般的)。フレームを積みすぎてこの上限を超えると、スタックオーバーフロー でプログラムが落ちます。

典型的な原因は 終了条件のない(または深すぎる)再帰 です。

factorial(n) を「n == 0 で止める」条件なしに書くと…
factorial(3)
 └ factorial(2)
    └ factorial(1)
       └ factorial(0)
          └ factorial(-1)
             └ factorial(-2)   ← 止まらずフレームが積み続ける → 💥 オーバーフロー

巨大な配列をローカル変数(スタック上)に取る、再帰が深くなりすぎる、といったケースも該当します。深い再帰が必要なら ループに書き換える、あるいは ヒープに状態を持つ ことで回避できます。

“無限ループ”とは落ち方が違う

無限ループは CPU を食い続けるだけで(普通は)落ちませんが、無限再帰はスタックを食いつぶして即クラッシュします。「再帰には必ず終了条件(ベースケース)を置く」——これが鉄則です。

つまずきポイント②:メモリリーク

メモリリーク とは、もう使わないのに解放されず、使用メモリが増え続ける 状態です。ヒープで起きます。短時間では気づきにくく、長時間動くサーバやアプリで少しずつメモリを圧迫し、最終的に性能劣化やクラッシュ(OS による強制終了=OOM Kill)を招きます。

よくある原因:

  • 手動管理free/delete の呼び忘れ、エラー時に解放処理を通らないパス。
  • GC 管理:グローバル変数・静的コレクションへの溜め込み、解除し忘れたコールバック/リスナ、意図せず残る クロージャ の参照。
リーク以外の“じわじわ”もある

ヒープを長く使うと、確保と解放を繰り返すうちに空き領域が細切れになる 断片化(フラグメンテーション) が起こり、「合計の空きは足りるのに大きな塊が取れない」状態になることがあります。リークとは別物ですが、これも長寿命プロセスがメモリで苦しむ原因の一つです。

まとめ

  • メモリは コード/データ/ヒープ/スタック。可変なのは ヒープとスタック
  • スタック:関数の出入りで自動・LIFO・高速。寿命は関数の中だけ。積みすぎると スタックオーバーフロー
  • ヒープ:動的確保で自由・長寿命。手動 or GC で管理し、放置すると メモリリーク
  • どちらに置くかは 言語・処理系が決める。仕組みを知っておくと、落ち方やメモリ肥大の原因を切り分けられます。

メモリ空間そのものがどう作られ、物理メモリと結び付くのかは 仮想メモリ を、実行単位とメモリ共有の関係は プロセスとスレッド を合わせて読むと、全体像がつながります。

OS Article

メモリ管理(スタックとヒープ)を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

メモリ管理

比較で見る軸

難易度: intermediate / カテゴリ: OS / タグ数: 4

導入後に効く点

スタックは 自動・LIFO・高速:関数呼び出しごとに積み下げされ、関数を抜けると自動で解放。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
intermediate
カテゴリ
OS
タグ数
4

判断チェックリスト

  • 自社の用途が「メモリ管理 / スタック」に近いか確認する。
  • 強みである「プロセスのメモリは コード/データ/ヒープ/スタック の4領域。可変なのはヒープとスタック。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

メモリ管理スタックヒーププロセスメモリ管理スタックヒーププロセス
参考: 公式情報