誰でもわかるメモリ管理入門:03

*この投稿は[誰でもわかるメモリ管理入門:02](https://qiita.com/segavvy/items/93dccb21c44b27176582)の続きです。
はじめての方は[誰でもわかるメモリ管理入門:01](https://qiita.com/segavvy/items/9a9f8aa5cc4e6760307a)からご覧ください。*

## 倉庫への往復は時間がかかる

大家さんは住人（プログラム）に頼まれて、しょうちゅう部屋（メモリ）の荷物（データ）を倉庫（ファイル）に運んだり、逆に倉庫の荷物を部屋へ運んだりしています。この投稿の1回目の[ファイルの管理](https://qiita.com/segavvy/items/9a9f8aa5cc4e6760307a#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E7%AE%A1%E7%90%86)で解説したように、ファイルの読み書きがこの形になります。

![倉庫.png](https://qiita-image-store.s3.amazonaws.com/0/139624/641b1220-ff1a-745d-e0f1-70113d9693d1.png)

いつもの仕事なので大家さんにとっては朝飯前なのですが、倉庫から運ぶにはどうしても時間がかかります。住人を待たせてしまうことにもなりますから、なるべく待たせる時間を減らしたいものです。

## 倉庫から同じ荷物を何回も運ぶのは無駄

ある日、住人のAさんに頼まれて、同じ荷物を何回も倉庫から運ぶことがありました。また別の日には、BさんとCさんとDさんが同じ荷物を頼んできて、やっぱり同じ荷物を何回も倉庫から運ぶことになりました。

ここで大家さんはひらめきます。何回も同じ荷物を倉庫から運ぶのは無駄なので、1回運んできた荷物は空いてる部屋にでも置いておきましょう。そうすれば次に同じ荷物を頼まれた時に、わざわざ倉庫まで取りに行く必要がありません。部屋へすぐに荷物を届けることができます。

![一時置き場.png](https://qiita-image-store.s3.amazonaws.com/0/139624/9cee0b2e-51ac-562d-1128-bd176280eb79.png)


ただ、その荷物の一時置き場にしている部屋がいっぱいになってしまったら、それ以上は荷物を追加できません。そこで、いっぱいになった時は、大家さんの勘で一番使われなさそうな荷物をそこから追い出すことにしました。

もちろん一時置き場を作っても、毎回違う荷物を頼まれ続けたら荷物は再利用されないので意味がありません。でも、何回も頼まれる荷物というのは意外に多いものです。さっそく試してみたところ、なかなか良い感じになることがわかりました。

こうして出来上がった荷物の一時置き場を使う仕組みが「ファイル システム キャッシュ」で、倉庫から1度運んだ荷物を一時置き場に置いて再利用する仕組みが「読み込みキャッシュ」です。

## 倉庫には後で運んでも構わない

大家さんは倉庫へ荷物を運ぶ依頼もたくさん受けます。依頼を受けたら荷物を倉庫に運んで住人に「運びました」と結果報告します。

ここでも大家さんはひらめきました。荷物は倉庫へ運んでしまう訳ですから、住人は運んだ倉庫の荷物をすぐには使いません。そこで、荷物をすぐに倉庫へ運ぶのではなく一時置き場に移動しておいて、とりあえず「運びました」とウソの報告をしておき、手が空いた時に一時置き場から倉庫へまとめて運べば効率的です。

ウソの報告には少し心が痛みますが、住人は待たされずに「運びました」と返ってくるので大喜びです。誰にも迷惑はかかりませんから、良しとしましょう。

こうして出来上がった仕組みが「書き込みキャッシュ」です。また、一時置き場から倉庫へまとめて運ぶ作業を「フラッシュ」と呼びます。

## 部屋のやりくり

[前回](https://qiita.com/segavvy/items/93dccb21c44b27176582)、外出している住人や寝ている住人の荷物を倉庫に運んで部屋を空けたり戻したりする仕組み（メモリ スワッピング）をご説明しましたが、今回ご説明したように、部屋は住人に貸し出すだけでなく荷物の一時置き場にも使います。

この時の部屋の使い方の優先順位ですが、一般的には住人への貸し出しを優先し、空き部屋がある場合に限り荷物の一時置き場に使います。部屋が足りなくなった時は一時置き場をどんどん減らします。空き部屋がなければ一時置き場はほとんどなくなります。

それでも部屋が足りない場合、[前回](https://qiita.com/segavvy/items/93dccb21c44b27176582)は「外出している住人や寝ている住人の荷物を倉庫に運んで」とご説明しましたが、活動中の住人に対しても強制的に眠らせて倉庫に荷物を運び、部屋を空けさせます。でも、いつまでも勝手に眠らせておく訳にはいかないので、またすぐに他の住人を追い出してその部屋に入れて目を覚させます。こんなことを繰り返すことになるので、倉庫の往復が多発し、住人は強制的に寝たり起きたりを繰り返させられてパフォーマンスが極端に悪化します。この現象を「スラッシング」と呼んだりします。

# 例えの整理
ここまでの例えを整理します。前回の表に、今回出てきたものを追加しました。

| 例え       | 実際 |
|:-----------------:|:------------------:|
| 賃貸アパートの大家さん|OS|             
|アパートの部屋|メモリ| 
|倉庫|ファイル|
|部屋を借りる住人|プログラム|
|荷物|プログラムが使うデータ|
|部屋の中での生活|プログラムの処理|
|架空の部屋番号を割り振るアイデア|仮想記憶|
|実際の部屋番号|メモリの物理アドレス|
|大家さんが住人に割り振る架空の部屋番号|メモリの仮想アドレス|
|外出している住人や寝ている住人の荷物を倉庫に運んで部屋を空けたり戻したりするアイデア|メモリ スワッピング|
|大家さんが外出している住人や寝ている住人の荷物を倉庫に運んで部屋を空ける作業|スワップ アウト|
|大家さんが倉庫に運んでいた住人の荷物を部屋に戻して元どおりにする作業|スワップ イン|
|倉庫によけた住人の部屋の荷物|スワップ ファイル<br/>／スワップ パーティション|
|:new: 荷物の一時置き場やそれを使う仕組み|ファイル システム キャッシュ|
|:new: 倉庫から1度運んだ荷物を一時置き場に置いておき再利用する仕組み|読み込みキャッシュ|
|:new: 荷物を倉庫へすぐには運ばず、一時置き場に置いて後で運ぶ仕組み|書き込みキャッシュ|
|:new: 一時置き場に置いていた荷物を、倉庫に一気に運ぶ作業|フラッシュ|
|:new: 部屋が足りず倉庫への往復が頻発してパフォーマンスが極端に悪化する現象|スラッシング|

# 今回のポイント

ファイル システム キャッシュによる読み込みキャッシュと書き込みキャッシュで、OSはファイルの読み書きを高速化してくれます。ただし、このファイル システム キャッシュの特性を知っておかないと、思わぬパフォーマンスの問題やデータ損失を招くことがあります。その辺りについて簡単にまとめます。

## ファイル システム キャッシュが十分に確保されているか

部屋のやりくりのところでも少しお話しましたが、OSはファイル システム キャッシュよりもプログラムを優先してメモリを割り当てます。メモリに余裕がないとファイル システム キャッシュにはメモリが割り当てられなくなり、ファイル アクセスのパフォーマンスが悪化してしまいます。

ややこしいのは、ファイル システム キャッシュの使用量がOSのメモリ使用量の合計値には含まれていないことです。OSのメモリの空き容量を見て「まだメモリに余裕がある」と思っていても、実はメモリが足りずファイル システム キャッシュの量が小さくなっていて、パフォーマンスが悪化していることがあります。この辺は次回以降で、もう少しまとめてみようと思います。

## パフォーマンスの把握が難しい

ファイル システム キャッシュはパフォーマンスの計測を難しくする一因です。

例えば100MBのファイルを読み込んで簡単な計算を行うプログラムを開発したとします。このプログラムの性能を知るために処理時間を計測したい場合、どのように計測すれば良いでしょうか。

おそらく1回目の実行と2回目以降の実行では、読み込みキャッシュが効くため2回目以降の方がかなり早くなります。この時、ファイル システム キャッシュの影響を排除するために1回目の計測値だけを採用することもできますし、逆にファイル システム キャッシュが効いた状態の方が正しいと考えて2回目以降の計測結果を使うこともできるでしょう。全部を使って平均やパーセンタイルといった話もあるかも知れません。

しかし、ファイル システム キャッシュの特性（他に動くプログラムのファイル アクセスに影響され、他に動くプログラムのメモリの使用状況にも影響される）を考えると、ファイル アクセスが主体のプログラムを単独で計測すること自体が間違いかも知れません。ファイル システム キャッシュに限らず、最近のシステムは仮想化や並列化などによってパフォーマンスの予測が難しくなっています。本番環境を想定し、それに適した計測をすることが重要だと思います。

## 書き込みキャッシュでデータが失われるリスク

USBのメモリやディスクを取り外す時は、そのOSの正規の手順を踏む必要があります。それをやらずにいきなり取り外すとデータが失われることがあります。失われてしまう原因の1つが、今回ご説明した書き込みキャッシュです。

プログラムがファイルに書き込んでも、そのデータはまだファイル システム キャッシュにあるだけで、実際のファイルに書き込まれていないことがあります。きちんと正規の手順を踏めば、その時にファイル システム キャッシュに残っているデータをファイルへフラッシュしてくれます。

なお、これは停電などの場合も同様です。いきなり電源が落ちると、データがまだファイルに書き込まれておらず、データが失われてしまうことがあります。

このように書き込みキャッシュにはデータが失われてしまうリスクがあるため、デフォルトでは無効になっている場合もあります。有効にする時はデータ消失のリスクを覚悟する必要があります。

![データ消失のリスク.PNG](https://qiita-image-store.s3.amazonaws.com/0/139624/d08bc02b-cfb8-5634-a1fb-2b0370cbe740.png)

## ファイル システム キャッシュを意識したプログラミング

大量のファイルを取り扱い、かつパフォーマンスも重要なプログラムを開発される場合は、ファイル システム キャッシュを経由させないファイル アクセスを考える必要があるかも知れません。

例えば、自分のプログラムも他のプログラムもしばらく読み込まないことがわかっている大量のファイルを普通に読み込んでしまうと、それだけでファイル システム キャッシュが埋め尽くされてしまい、本来再利用されるべきデータが追い出されてパフォーマンスが悪化してしまいます。この辺のお話は、もしこのメモリ管理入門が長続きするようであれば触れてみたいと思います。

今回は以上です。
次回は、主要なOSにおけるメモリの使用状況の確認方法について、簡単にまとめてみたいと思います。
（次回がいつになるかはわかりません。気長にお待ちください……）