スラッシング

スラッシング (英: Thrashing) とは、仮想記憶環境下において、物理メモリが不足気味で、かつ動作しているプロセスのアクセスパターンのために、ページアウトしたデータをすぐにページインするというようなことを頻繁に繰返す必要が発生していて、仮想記憶を管理しているシステム（一般にはオペレーティングシステム）のそのような動作の結果、システムの動作が極端に遅くなっている、というような状態のことである。 多くの物理メモリを大量に必要とするプロセスが多数走っているような物理メモリ不足の状態で、さらに各プロセスのアクセスパターンに依るが、ページイン（ディスクからページを読み込む）とページアウト（ディスクにページを書き込む）が極端に高頻度に発生し、オペレーティングシステム (OS) がページング（ページイン、ページアウトの両者を指す）に処理能力を費やしてしまい、アプリケーションの動作を妨げてしまっている状態になる（正確には、OSは入出力コマンドをディスクコントローラに発行してほとんどの時間は待っているだけであり、処理能力的には正確には暇である）。この状態から脱出するには、メモリを大量消費しているアプリケーションを凍結または終了させるしかないが、コマンドの受付すらも困難な状態であることが多い。 多くの実装において、仮想記憶のスワップ領域の本体は、ハードディスク上に作られたほぼ連続した領域であることが多い。しかし、この領域の連続性は仮想記憶ではアクセス場所を特定するための処理以外ではあまり大きな意味を持たない。仮想記憶を保存している領域へのアクセスは、仮想記憶へのページ割り当てが開始され始めた時点を除き、運用状態ではほぼランダムなものになる。このため、ページングはハードディスクにとって最大のアクセスタイムを伴う「常にシーク動作を伴う読み書き」であり、それを実行するのに必要な時間は高速なものでも数ミリ秒、一般的なハードディスクでは十数ミリ秒から数十ミリ秒必要とする。これはRAMへのアクセス時間の106 - 107倍であり、それによりスラッシング中においてプログラム（OSを含む）の実行速度は極度に低下する。このため、スラッシング状態に陥らないことがOSの可用性確保上重要な課題となる。 多くのUNIXではユーザーが確保できるメモリ量を制限する機能がある。資源の平等な分配はもちろん、管理者がコンピューターのコントロールを失うという最悪の事態を避けるためでもある。Microsoft Windows NT系のOSでは、ページファイルを使用しなければメモリを確保できない状態になると、システムコールのメモリ確保関数は、ページングによってメモリが確保されるまで要求を受け付けずエラーにし、理由として物理メモリに空きがないことを通知し、なるべくページファイルを使わないようアプリケーションに促す。アプリケーションはこの情報を元に不必要なメモリをOSに返却したり、あるいはメモリを必要とした操作を却下することができる。もちろん、ページングが成功するまで待機し、仮想記憶によって取得されたメモリ空間を利用することもできる。しかし、この方法は最悪の場合OS自体がページアウトされる危険性があり、システムの著しい速度低下を招き、ユーザーには歓迎されない結果をもたらすだろう。 近年においてスラッシングは比較的容易に回避可能な現象となった。理由としては、 メモリの低廉化・高密度化によって必要十分な物理メモリを準備することができるようになった 共有ライブラリによってアプリケーションのメモリ利用効率が向上した よりよいワーキングセット・ページ置換アルゴリズムの採用によって仮想記憶を実現する手法が効率的になった ハードディスクの記録密度向上によりI/O時間が高速化された バスマスタI/OによりCPU時間を消費せずディスクI/Oができるようになった カーネルのマルチスレッド化によってページフォールトを起こしたプロセス以外のプロセスをブロックしなくなった 積極的なページのクリーニングによりディスクI/Oを伴わないページ獲得が期待できるようになった 等があげられる。 ただし、上記の理由としてあげた要件を満たさない場合や、例えば物理メモリが1GBしかないのに2GBのメモリを使うアプリケーションを動かした等の場合は、依然としてスラッシングが起きうる。近年は高画素の画像データや動画、3Dゲームなど、一般向けの用途でもメモリ消費が増加しており、スラッシングの発生率は再び増加傾向にあると考えられる。 スラッシングを完全に予防するには、仮想記憶を使わないという戦略がある。多くのリアルタイムオペレーティングシステムが仮想記憶を使わないのは、スラッシングはもとより、ページングによる遅延を避けるためである。一般的なヒープ管理アルゴリズムではメモリのフラグメンテーションによって消費される使用されているメモリと使用されていないメモリの合計は、使用されているメモリの2倍を超えない。それは、使用されていないメモリ断片が使用したいメモリ断片よりも大きければ、その使用されていないメモリ断片は使用されるからである。よって使用するメモリ見込み量の2倍以上物理メモリがあれば仮想記憶を準備する必要はない。ただし、メモリオーバーフローが全く起きえない、またはメモリオーバーフローのエラー処理を全てのプログラムが適切に行う前提であり、そもそも適切な処理方法が難しいアプリケーションの存在も考慮した上で慎重に選択すべき戦略である。スラッシングではないただのページングであれば、リアルタイム性を重視しないOSでは問題にならない。 キャッシュメモリを使用するパターンによっては、頻繁に同じキャッシュラインを別の用途(つまり別の物理アドレスに対応する内容)で置き換えてしまう現象が発生する。これをキャッシュメモリのスラッシング（キャッシュスラッシング）と呼ぶ。例えば、128Kバイトの配列 A の各要素に何らかの演算を施して、同じサイズの配列 B に結果を格納していく場合を考えてみよう。一般に、A[0]からデータを読み込むとメモリ上の A[0] を含むある程度のサイズの内容がキャッシュに取り込まれる。これによって A[1]、A[2]とアクセスしたときにキャッシュメモリにヒットしてCPUがその性能をフルに発揮できるようになる。しかしここで、A と B が物理メモリ上で隣接かつ連続していて、キャッシュメモリも 128Kバイトだったとする(仮想記憶方式では現実には起こりにくいが)。プログラムは A の先頭からデータを取り出し、計算を施して B の先頭に書き込む。しかし、この仮定のような配置であった場合は A から読み込んだデータと B に書き込むデータはキャッシュメモリ上の同じ位置（ライン）になってしまうため、ループするたびに二回主記憶にアクセスせざるを得なくなり、性能は劇的に低下する。仮想記憶方式であったとしても、キャッシュが仮想インデックス方式であった場合は同じことが容易に発生する。このとき、キャッシュメモリがセットアソシアティブ方式やフルアソシアティブ方式であれば、この問題をほぼ回避できる。 TLBでもスラッシングが発生する場合がある。TLBはいわばページテーブルのキャッシュのようなものであり、短時間に多数のページにアクセスするプログラムを実行するとTLBミスが多発し、その解決に時間を費やすことになる。これをTLBのスラッシングと呼ぶ。このときには当然ながらキャッシュヒット率の低下を伴うことが想定される。このような事態はオブジェクト指向プログラミングが盛んになって、発生する可能性が高くなった。局在的なメモリアクセスと、そうではないキャッシュミスを起こすメモリアクセスの比率が変わった。プロセッサー設計者はこの時代の変遷を読み取り、より大きなキャッシュメモリやTLBを搭載すること、アクセスするであろうメモリの予測と投機的実行を実装した。 しかしキャッシュメモリとTLBなどのプロセッサ側だけの対応だけでは十分ではなく、またプロセッサの巨大化と高価なキャッシュメモリの増大によるコスト増、それらが消費する電力と熱の問題が深刻になった（例えばインテルのXeonプロセッサの中には、汎用機のCPUに匹敵する巨大なパッケージに巨大なヒートシンクを取り付けたものもあった）。 この問題を解決すべく一次記憶の高速化とそれを結ぶバスの強化が行われている。CPUバスの駆動周波数が1GHzを超え10Gbyte/秒もの帯域を持つプロセッサも存在する。 ^ スワップファイル、ページファイル、スワップパーティション、ページング領域など名称は実装により異なる。 ^ ただし、Windowsのような動的にスワップ領域を確保・拡大できる実装では連続性はあまり期待できない。 ^ GNU mallocアルゴリズムでは適用できない場合がある。 ^ 具体的には、物理メモリアドレスの上位ビット（キャッシュメモリでのフレームアドレス）がAとBとで異なり、かつ、下位ビット（エントリアドレス）が、同一要素番号のAとBにつき同一となるような場合。 ^ 同一エントリに異なるフレームアドレスが転送されると必ずラインの入れ替えが発生するため。キャッシュメモリのダイレクトマップ方式参照。 リソース競合