資格道場 › レッスン一覧 › ソフトウェア
ソフトウェア・5/10・12分

主記憶の割当てと断片化・解放

ゴール

主記憶の割当てと解放で起きる3つの困りごと(細切れ=フラグメンテーション、返し忘れ=メモリリーク、参照されない残骸=ガーベジ)と、その対策の用語を区別できるようになる。あわせて、動的再配置のメモリアクセス時間の計算と、可変区画方式の空き領域数のトレースを手順どおりに解けるようになる。

プログラムは動きながらメモリを借りて返すのを繰り返すので、返し方を間違えると「空きはあるのに使えない」「だんだん重くなる」障害が起きる。試験では用語の定義を問う4択と、手を動かす計算・トレース問題の両方で出る。

このレッスンで稽古する(用語チェック・過去問)

結論:メモリの困りごとは3種類、対策も3種類

主記憶(CPUが直接読み書きするメモリ)は、プログラムが必要なときに割り当て、使い終わったら解放する。ここで起きる困りごとと対策は、セットで覚えると混同しない。

困りごと(現象) 何が起きるか 対策(手法)
フラグメンテーション(断片化) 空きが細切れになり、合計は足りるのに大きな連続領域が取れない コンパクション(使用中の領域を詰めて空きを1つにまとめる)
メモリリーク 確保した領域が解放されず、使える量がどんどん減る バグを直す(解放漏れをなくす)。再起動は一時しのぎ
どこからも使われない領域(ガーベジ=ごみ) 誰も参照していないのに場所を取っている ガーベジコレクション(不要領域を自動で回収する)

試験の選択肢は「現象の名前」と「対策の名前」を入れ替えてくることが多い。現象か手法かを先に判断するのが最初の一手になる。

メモリリーク:確保したまま返さない(最頻出)

メモリリークは、OSやアプリのバグで、動作中に確保した主記憶が解放されずに残り続ける現象。図書館の本を借りたまま返さない利用者がいると、棚の本が少しずつ減っていくのと同じで、長時間動かすほど利用可能なメモリが減り、最後は動作が遅くなったり止まったりする。

試験では「メモリリークの説明はどれか」という4択がほぼ同じ形で繰り返し出ている。正解以外の3つは別の用語の説明なので、下の表で見分けられれば確実に取れる。

選択肢の言い方 正体
確保した主記憶が解放されず、利用可能な部分が減る メモリリーク(正解)
同時実行数を増やすと主記憶が足りず、処理時間のほとんどがページングに費やされてスループットが極端に落ちる スラッシング
プログラム領域に制限があるとき、必要になったモジュールを主記憶に取り込む オーバレイ
空きの総量は足りるのに不連続で散らばり、大きなプログラムを置けない フラグメンテーション(外部断片化)

キーワードで見分けると速い。「解放されない」→リーク、「ページングばかり」→スラッシング、「必要なモジュールを取り込む」→オーバレイ、「総量は足りるのに不連続」→フラグメンテーション。

時間の経過起動直後使用中空きしばらく動かした後使用中返し忘れ空き長時間動かした後使用中返し忘れ空きさらに動かし続ける空きほぼ0使用中返し忘れ最後は動作が遅くなる・止まる
メモリリークでは返し忘れた領域が時間とともに増え、空きがほぼ0になると遅くなったり止まったりする。

メモリプール:固定長方式と可変長方式の比較

メモリプールは、あらかじめまとめて確保しておいたメモリを、プログラムの求めに応じて貸し出し・回収する仕組み。貸し出す単位の決め方で2つの方式がある。

観点 固定長方式 可変長方式
貸し出す単位 決まった大きさのブロック 要求された大きさぴったり
メモリ効率 悪い(小さい要求でもブロック1個分を使い、余りが無駄になる=内部断片化) 良い(必要な分だけ使う)
獲得・返却の速さ 速く一定(空きブロックを1つ渡すだけ) 遅く不定(合う空きを探す・隣と合体させる処理がいる)
起きやすい断片化 内部断片化(ブロック内の余り) 外部断片化(ブロック間の細切れ)

覚え方は「固定長=効率は悪いが速く一定」の1行。リアルタイムシステム(決められた時間内に応答が必要な仕組み)では、処理時間が読める固定長方式が好まれる、という理由とセットにすると忘れにくい。

選択肢は「効率(良い/悪い)」×「速度(速い/遅い)」×「一定/不定」の組合せで4つ作られる。固定長は悪い・速い・一定、可変長は良い・遅い・不定と、全部が反対になっていると押さえる。

固定長方式使用余り使用余り使用余り使用余り同じ大きさのブロックを1個ずつ貸す余り=内部断片化/効率は悪い・速く一定可変長方式使用使用使用空き隙間隙間要求ぴったり/隙間=外部断片化効率は良い・遅く不定色付き=使用 白=未使用
固定長方式は同じ大きさのブロックを1個ずつ貸すのでブロック内に余り(内部断片化)が出るが速く一定、可変長方式は要求ぴったりで貸すがブロック間に隙間(外部断片化)が出て遅く不定。

フラグメンテーション:空きはあるのに使えない

フラグメンテーション(断片化)は、割当てと解放を繰り返すうちに、空き領域が細切れになって散らばる現象。駐車場で車がまばらに停まっていると、空きの台数は十分でもバスを停める連続スペースがない、という状態に近い。

ポイントは「合計では足りても、必要な連続領域が確保できない」こと。対策はコンパクション(使用中の領域を片側に寄せて空きを1つにまとめる)で、そのとき領域が移動するのでアドレスの補正(次の節の動的再配置)が必要になる。

同じ考え方はファイルにもある。ディスク上でファイルが小さく分断された領域に置かれるとアクセス効率が落ちるので、対策はファイルと空きを連続した領域に置き直すこと(デフラグ)。

コンパクション前(断片化)使用中空き使用中空き使用中空き大きなプログラム× 入らない空きの合計は足りるが、連続していない使用中を片側に詰めるコンパクション後使用中使用中使用中空き(1つにまとまる)大きなプログラム○ 置ける移動するのでアドレス補正(動的再配置)
コンパクション前は空きの合計が足りても連続していないので大きなプログラムが入らず、使用中を片側に詰めると空きが1つにまとまって置けるようになる。

再配置と動的再配置

再配置は、プログラムを主記憶に読み込んだとき、実際に置かれた位置(ロード位置)に合わせてプログラム内のアドレス情報を補正すること。似た語感の「再コンパイル(作り直し)」「リロード(読み込み直し)」とは別物。

動的再配置は、プログラムの実行中に領域を移動させ、それに合わせてアドレスを補正すること。コンパクションで空きをまとめるときに使う。計算問題では「何バイト動かすか」と「読取りと書込みの両方がいる」の2点がカギになる(例題1)。

可変区画方式とガーベジコレクション

可変区画方式は、プログラム(モジュール)の大きさに合わせて主記憶を区切って割り当てる方式。試験では「空き領域の先頭から置く」というルールで、ロードと解放を順にたどって空きが何か所残るかを数えさせる(例題2)。解放した領域が隣の空きとつながったら1か所として数えるのを忘れないこと。

ガーベジコレクションは、動的に確保された後、どこからも参照されなくなった領域を回収して再び使えるようにする機能。選択肢に並ぶヒープ(動的に割り当てる領域そのもの)、スタック(後入れ先出しの領域)、フラグメンテーション(現象の名前)は、どれも「回収する機能」ではない。

回収前プログラムヒープ参照されている参照されているどこからも参照なしガーベジコレクションが回収回収後プログラムヒープ参照されている参照されている空き(再び使える)参照されない領域だけが空きに戻る
ガーベジコレクションは、ヒープの中でどこからも参照されなくなった領域だけを回収し、空きに戻して再び使えるようにする。
例題 1

動的再配置に必要なメモリアクセス時間を求める

図のメモリマップで,セグメント 2 が解放されたとき,セグメントを移動(動的再配置)し,分散する空き領域を集めて一つの連続領域にしたい。1 回のメモリアクセスは 4 バイト単位で行い,読取り,書込みがそれぞれ 30 ナノ秒とすると,動的再配置をするために必要なメモリアクセス時間は合計何ミリ秒か。ここで,1 k バイトは 1,000 バイトとし,動的再配置に要する時間以外のオーバヘッドは考慮しないものとする。
メモリマップ(左から右へ横一列): | セグメント 1 (500k バイト) | セグメント 2 (100k バイト) | セグメント 3 (800k バイト) | 空き (800k バイト) |

ア 1.5
イ 6.0
ウ 7.5
エ 12.0

出典:平成29年度 秋期 基本情報技術者試験 午前 問19

解き方と答えを見る
  1. メモリマップを確認する:左から セグメント1(500k) / セグメント2(100k) / セグメント3(800k) / 空き(800k)。
  2. セグメント2を解放すると、空きは「セグメント1とセグメント3の間の100k」と「右端の800k」の2か所に分かれる。
  3. 空きを1か所にまとめるには、セグメント3(800kバイト)を左へ100k詰めればよい。セグメント1は左端にあるので動かす必要はない。
  4. 動かすデータ量は 800kバイト=800,000バイト(1kバイト=1,000バイトの条件)。
  5. 1回のアクセスは4バイト単位なので、アクセス回数は 800,000 ÷ 4 = 200,000回。
  6. 移動は「読み取ってから書き込む」ので、1単位あたり 読取り30ns + 書込み30ns = 60ns。
  7. 合計時間 = 200,000 × 60ns = 12,000,000ns。
  8. 単位を直す:12,000,000ns = 12,000μs = 12ms。

答え:エ:12.0ミリ秒(読取りだけで数えると6.0msの誤答になる)

例題 2

可変区画方式で最後に残る空き領域の数をトレースする

プログラムのモジュール単位に主記憶を割り当てる可変区画方式のコンピュータにおいて,五つのモジュール A~E を次の順序でロードしたり解放したりする。最後のモジュール E をロードした時点で,主記憶の空き領域は何か所存在するか。ここで,主記憶は 500 k バイトで初期状態では何もロードされていない。また,モジュールは空き領域の先頭からロードするものとし,その他の条件は考慮しない。 〔ロードと解放の順序〕 A のロード → B のロード → C のロード → B の解放 → D のロード → A の解放 → E のロード 〔モジュールの大きさ〕(表を figure_note に記載)
モジュールの大きさ モジュール | 大きさ(k バイト) A | 200 B | 100 C | 150 D | 80 E | 90

ア 3
イ 4
ウ 5
エ 6

出典:基本情報技術者試験 科目A免除 修了試験 20150125 実施分 問19

解き方と答えを見る
  1. 主記憶は0〜500k。大きさは A=200, B=100, C=150, D=80, E=90(kバイト)。ルールは「空き領域の先頭から置く」。
  2. Aのロード:A=0〜200。
  3. Bのロード:B=200〜300。
  4. Cのロード:C=300〜450。空きは450〜500。
  5. Bの解放:空きは 200〜300(100k)と 450〜500(50k)。
  6. Dのロード(80k):先頭側の空き200〜300に入る → D=200〜280。空きは 280〜300(20k)と 450〜500(50k)。
  7. Aの解放:0〜200が空く。空きは 0〜200、280〜300、450〜500 の3か所(それぞれ隣が使用中なので合体しない)。
  8. Eのロード(90k):先頭の空き0〜200に入る → E=0〜90。空きは 90〜200(110k)、280〜300(20k)、450〜500(50k)。
  9. 空き領域を数える:3か所。

答え:ア:3か所

要点(試験直前に読む)

  • メモリリーク=確保したまま解放されず、使える量が減る。4択の他の選択肢はスラッシング・オーバレイ・フラグメンテーション。
  • メモリプールの固定長方式は「効率は悪いが速く一定」、可変長方式は「効率は良いが遅く不定」。
  • フラグメンテーション=合計は足りるのに連続領域が取れない現象。対策はコンパクション(ファイルならデフラグ)。
  • ガーベジコレクション=参照されない領域を回収する機能。ヒープ・スタック・フラグメンテーションは機能ではない。
  • 動的再配置の時間は「動かすバイト数÷4バイト×(読取り+書込み)」で求める。例題は800,000÷4×60ns=12ms。
  • 可変区画方式のトレースは、使用中と空きの範囲を毎回書き出し、隣どうしの空きは合体して1か所と数える。

ひっかけ

  • 動的再配置で読取り時間だけを数えると6.0msになる。移動は「読んで書く」ので必ず2倍する。
  • 動的再配置で、左端にあって動かす必要のないセグメント1まで移動量に入れてしまう。解放された領域より右にある領域だけが移動対象。
  • メモリリークを問う4択で「総量は足りるのに不連続」を選んでしまう。それはフラグメンテーション。「解放されない」がリークの目印。
  • 現象と手法の取り違え:フラグメンテーション(現象)とコンパクション(対策)、ガーベジ(不要領域)とガーベジコレクション(回収機能)を入れ替えた選択肢に注意。
  • 「可変長方式なら断片化しない」「返却のたびにガーベジコレクションが必要」は誤り。断片化はどちらの方式でも起こりうる。

用語(17)

再配置 さいはいち
プログラムを主記憶に読み込んだとき、置かれた位置に合わせてプログラム内のアドレスを補正すること。
⚠ 再コンパイル(ソースから作り直す)・リロード(読み込み直す)と語感が似ているが、アドレスの補正をするのは再配置だけ。
動的再配置 どうてきさいはいち
実行中にプログラムの領域を移動させ、それに合わせてアドレスを補正すること。
⚠ ロード時に一度だけ補正する(静的な)再配置との違いは「実行中に動かすかどうか」。
可変区画方式 かへんくかくほうしき
プログラムの大きさに合わせて主記憶を区切って割り当てる方式。
⚠ 固定区画方式は区画の大きさが最初から決まっている。可変区画方式は外部断片化が起きやすい。
フラグメンテーション fragmentation(断片化)
割当てと解放を繰り返すうちに空き領域が細切れになり、合計は足りても連続領域が取れなくなる現象。
⚠ コンパクションは解消する手法、メモリリークは解放されない現象。フラグメンテーションは「現象」で、空きはちゃんと返されている。
内部断片化 ないぶだんぺんか
割り当てた区画やブロックの中で、使われずに余る部分が生じること。
⚠ 外部断片化は区画と区画の間の空きが細切れになること。固定長方式は内部、可変長方式は外部が起きやすい。
外部断片化 がいぶだんぺんか
使用中の領域の間に小さな空きが散らばり、大きな連続領域を確保できなくなること。
⚠ 内部断片化(割り当てたブロック内の余り)と区別する。
コンパクション compaction
使用中の領域を詰めて移動し、散らばった空き領域を1つの連続領域にまとめること。
⚠ フラグメンテーション(現象)の対策の名前。ガーベジコレクション(参照されない領域の回収)とも別物。
デフラグ defragmentation
ディスク上で分断されたファイルや空き領域を連続した領域に置き直し、アクセス効率を戻すこと。
⚠ 主記憶ではなくファイル(ディスク)の断片化対策を指すことが多い。
メモリプール memory pool
あらかじめ確保したメモリを、要求に応じて貸し出し・回収する管理の仕組み。
固定長方式(メモリプール) こていちょうほうしき
決まった大きさのブロック単位で貸し出す方式で、メモリ効率は悪いが獲得・返却は速く一定の時間で済む。
⚠ 可変長方式は効率が良いが処理が遅く時間が不定。性質が全部反対になる。
可変長方式(メモリプール) かへんちょうほうしき
要求された大きさぴったりで貸し出す方式で、メモリ効率は良いが獲得・返却が遅く処理時間が不定になる。
⚠ 「可変長なら断片化しない」は誤り。外部断片化は起きる。
メモリリーク memory leak
バグなどで確保した主記憶が解放されず、利用できるメモリが減り続ける現象。
⚠ フラグメンテーションは空きが返されているのに細切れで使えない状態。リークはそもそも返されていない。
ガーベジコレクション garbage collection
どこからも参照されなくなった記憶領域を自動で回収し、再び使えるようにする機能。
⚠ ヒープ(領域そのもの)、スタック(後入れ先出しの領域)、フラグメンテーション(現象)は回収機能ではない。
ヒープ heap
プログラムの実行中に、必要な大きさを動的に割り当てるための記憶領域。
⚠ スタックは後入れ先出しで自動的に積み下ろしする領域。ガーベジコレクションの対象になるのはヒープ側。
スタック stack
最後に入れたものから先に取り出す(後入れ先出し)形で使う記憶領域。
⚠ ヒープ(自由な順で動的に割当て)と区別する。
スラッシング thrashing
主記憶が足りずページの入れ替え(ページング)ばかりが起き、スループットが極端に落ちる現象。
⚠ メモリリークの4択で「処理時間のほとんどがページング」とあればスラッシング。
オーバレイ overlay
プログラム全体が入りきらないとき、必要になったモジュールだけを主記憶に取り込んで実行する手法。
⚠ メモリリークの4択の「必要になったモジュールを取り込む」はオーバレイの説明。

このレッスンの過去問

このレッスンで稽古する