Linuxでは限られたメモリを最大限に活かし、仮想メモリなどの仕組みを使って最大限のパフォーマンスを発揮できるようになっています。その反面、実際にどれくらいの物理メモリを消費しているのかといった情報が分かりにくく、開発者にとっては例えばメモリリークの特定が難しい場合もあります。

そこで今回はLinuxのメモリの使用量について確認する方法を紹介します。さらに、動的テストツールによる動的解析と組み合わせて使用することにより、ターゲット機器が実行された際のメモリ使用量の監視に加えて、実行されていた処理と照らし合わせて解析する方法を紹介します。

 

Linuxのメモリ管理について

 

proc/meminfoで見るメモリ情報

Linux上でメモリの使用状況を取得するコマンドは「free」「top」「ps」などがありますが、これらは「proc/meminfo」の情報を使用しています。「proc/meminfo」を直接参照することで、システム全体の詳細なメモリの使用状況を把握することが可能です。実際に情報を取得すると以下のようにメモリに関する様々な情報が出力されます。なお、カーネルのバージョンは4.9です。

 

どれくらいのメモリを使用しているのか

この出力の「MemTotal」から「MemAvailable」を差し引いた値が、現在ターゲットとするシステムが使用しているメモリサイズです。

「MemTotal」がターゲット機器の物理メモリのサイズです。値を確認してみると搭載されているRAMのサイズと異なりますが、搭載しているRAMのサイズからカーネルのバイナリコードなどを差し引いた値となっています。詳細な処理はアーキテクチャによって異なりますが、ターゲット機器を起動した際に計算されます。起動後、ブート処理で使用した後に使われないページのサイズなども足し合わされ、その後は変化しません。

「MemAvailable」とは、その名の通り「使用可能なメモリサイズ」で「free」コマンドの「available」フィールドと同じです。この値はキャッシュや再利用可能なメモリ領域を考慮した上で、スワップすることなく使用できるサイズを表しています。カーネルのコードで実際に行われている計算では、カーネル内部で管理しているLRU(Least Recently Used = ページに割り当てられたフレームを解放するため、直近で最も参照頻度が少ないものを選ぶカーネルのアルゴリズム)のリストからそのサイズを取得したり、「zone」というページの分割単位から「wmark_low(=watermarkという、この値を下回るとカーネルがメモリ不足と認識する危険水域)」を取得したりしています。

 

メモリ使用率の計算方法

Linuxでメモリ使用率を見る際、「Used」の数値だけを追うのは誤解のもとになることがあります。Linuxは、システムパフォーマンスを上げるため、空いているメモリを積極的にキャッシュやバッファに使う特性があるからです。本当に注目すべきは、ユーザーアプリケーションが自由に使える「空きメモリ」を示す 「MemAvailable」の数値です。この「実質的なメモリ使用率」は、以下の計算式で求められます。

実質的なメモリ使用率=((MemTotal−MemAvailable)/MemTotal)×100(%)

たとえば、「free -h」コマンドの出力や「/proc/meminfo」の内容からこれらの数値を取得し、上記の式に当てはめることで、より正確なメモリ使用率を把握することができます。

 

メモリ使用率の判断基準と注意点

では、メモリ使用率がどの程度なら正常で、どの程度から注意が必要なのでしょうか?これはシステムの種類(サーバー、組み込み機器、開発PCなど)や用途によって異なりますが、一般的な経験則としての目安は以下のようになっています。

  • 〜60%
    通常運転の範囲内と言えます。システムが適切にメモリを使用しています。
  • 60%〜80%
    少し注意が必要です。アプリケーションの負荷が高まっているか、メモリ使用量の最適化を検討する時期かもしれません。
  • 80%〜90%
    高負荷状態です。システムのパフォーマンス低下や、応答性の悪化が起き始める可能性があります。原因の調査を開始することをお勧めします。
  • 90%〜100%
    非常に危険な状態です。この状態が続くと、「OOM(Out Of Memory)Killer」が発動し、システムが自動的にプロセスを強制終了させる可能性があります。最悪の場合、システムがフリーズしたり不安定になったりすることもあります。
【判断のポイント】
  1. 瞬間的な上昇か、継続的な上昇か?
    一時的な高負荷によってメモリ使用率が瞬間的に跳ね上がる場合は問題ないことが多いです。しかし、長時間にわたって高水準を維持したり、徐々に上昇し続ける場合(メモリリークの可能性)は、特に注意が必要です。
  2. キャッシュの影響を理解する
    Linuxは空きメモリを積極的にキャッシュとして利用します。 そのため、「free」コマンドの出力で「used」が高く表示されていても、「buff/cache」が多い場合は、実際にアプリケーションが消費しているメモリ量は少ないことがあります。重要なのは、実際に利用可能なメモリを示す「MemAvailable」 の数値です。
  3. スワップ領域の利用状況
    スワップ領域が頻繁に利用されている場合、物理メモリが不足しているサインです。 この状態が続くと、ディスクアクセスが増え、システム全体のパフォーマンスに悪影響が出ますので、「free」コマンドの「Swap」行も併せて確認しましょう。
  4. 自身のシステムの「平常時」を知る
    最も重要なのは、使用しているLinuxシステムが普段どれくらいのメモリを使用しているかを把握しておくことです。これにより、異常な変化を素早く察知できます。
 

メモリ使用量の監視方法

これらの値をアプリケーションから定周期で参照することでメモリ使用量の監視を行います。メモリの使用量の計算はカーネル側で行う必要があるため、デバイスドライバを用意してその中で計算処理を行います。

 

メモリ使用量の取得処理

カーネル側では、「fs/proc/meminfo.c」の「meminfo_proc_show」というLinuxカーネル内の関数を参考にして、「MemTotal」と「MemAvailable」の値をデバイスドライバで取得します。今回はDT+のログ出力にも使用しているprocファイルシステムを使っており、「mem_trace_read」関数がメモリ使用量を計算している箇所になります。この処理はカーネルのバージョンによって異なり、以下の例はVer.4.6以降に対応した処理です。

/* メモリ使用量の取得 */
static ssize_t mem_trace_read(struct file *fp, char __user *buf, size_t count, loff_t *data)
{
	struct sysinfo i;
	long used_memsize;
	
	/* ページ数⇒キロバイトに変換 */
	#define K(x) ((x) << (PAGE_SHIFT - 10))		
	
	/* メモリの使用量を取得 */
	si_meminfo(&i);
	used_memsize = K(i.totalram - si_mem_available());
	copy_to_user((void*)buf, &used_memsize, sizeof(long));
	return 0;
}

static const struct file_operations mem_trace_proc_fops = {
	.read = mem_trace_read,
};

static int mem_trace_init(void)
{
	/* モジュールロード時の処理 */
}
static void dt_mem_trace_exit(void)
{
	/* モジュールアンロード時の処理 */
}

このようにモジュール化しておくことで、処理を追加したり変更する際にいちいちカーネルをビルドしなおす必要がないため便利です。また必要に応じてロード・アンロードできるため、デバッグ時のみロードしておくといった柔軟な使い方ができるようになります。

おさらい:ページとは?

コード上に「ページ数⇒キロバイトに変換」とありますが、ページとは仮想メモリ空間の固定長の分割単位です。仮想メモリ空間はこの固定長のブロック単位で物理メモリに割り当てられます。この固定長のサイズは一般的に4KByteがサポートされておりますが、CPUごとに異なります(カーネルコンフィグより選択することが可能)。これに対して物理メモリは「フレーム」という分割単位で領域を確保して、ページを割り当てていきます。データの読み書きが発生するページに対して公平・効率的にフレームが割り当てられるように、Linuxカーネルでは物理メモリの割り当てをコントロールしています。

デバイスドライバを呼び出すアプリケーション側のコードは以下のようになります。1sec周期で上記のデバイスドライバの処理を呼び出すようにしています。

#define MEM_TRACE_DRV    "/proc/mem_trace_proc"

void* thread_mem_output(void *state)
{
    struct timespec start, end;
    long memsize;
    while(!thread_kill)
    {
        clock_gettime(CLOCK_MONOTONIC, &amp;start); 

        /* メモリ使用量の取得 */
        read(dat_file, &amp;memsize, sizeof(memsize));

        clock_gettime(CLOCK_MONOTONIC, &amp;end);
        nDelay(0, 1000000000 - (end.tv_nsec - start.tv_nsec));  /* 1sec周期 */
    }
    printf("thread_mem_output exit");
    pthread_exit(NULL);
}

 

実際に実行してみる

実際に上記のカーネルをビルドしターゲットへと書き込み、アプリケーションを実行してみます。そうすると以下のようにアプリケーションを実行しながらリアルタイムにメモリの使用量を確認できます。

これにより、どういう操作をしたときにメモリの使用量が増加したのか、そして確保されたメモリが解放されたのか、といったことが分かるようになります。開発者としては、そのときに実行されていた処理や変数値といったソースコードに関する情報が知りたいところで、これらはズバリ「動的テストツール DT+」の得意とするところです。DT+を併用することでそういった情報と、メモリの使用状況を照らし合わせて確認できます。

 

DT+でリアルタイムに監視する

ハートランド・データといえば動的解析ツールDT+!
組込み機器の実機を動かし、「ナマの挙動」を解析する動的解析。DT+を使うと、ソフトどころかハード側の挙動まで全部ひと手間で解析できます。
メモリ問題だけでなく、開発現場の課題をどう解決し、どんな成果を生み出すか、製品ページで具体的な機能と導入事例をご確認ください。

DT+の詳細はこちら

「動的テストツール DT+」の詳細は上記のページに任せますが、ターゲットを実際に動作させ、その際の実行処理・経路や処理時間を解析できるツールです。豊富な解析機能によりデバッグや不具合解析を大幅に効率化できます。変数値を出力する機能もありますので、今回はこの機能を使ってメモリの使用量を出力します。

DT+を使用することによって「どの関数が実行されていた」「どの分岐に入った」といった情報が解析できます。これをメモリの使用量と合わせて確認することで、メモリ使用量がその値になった際に実行されていた処理を効率的に確認できます。DT+には変数値をグラフィカルに確認するための機能も用意されており(下図左側)、メモリの使用量の変化の確認もできます。今回は定周期でメモリの確保を行っていましたが、その様子がグラフから分かります。

ログのリストもグラフィカルに表示する機能がありますので(上図右側)、実行経路を俯瞰した形で確認できます。前後の処理の関連性を関数・スレッド・プロセス単位で確認できますので、処理に対する理解を深め現象を正確に把握することに役立ちます。

メモリ使用量の異常が発生している場合、それが一時的なものか、あるいはメモリリークによるものかを見極めることが重要です。DT+を活用すれば、こうした異常の兆候を可視化し、早期対応につなげることが可能です。
より詳しい原因特定やデバッグの進め方に関心がある方は、ぜひこちらの記事もご覧ください。

Linux開発で知っておきたいデバッグコマンド
GDB・gprof・MEMWATCHなど、開発現場で役立つデバッグコマンドを徹底解説

記事を読む
 

メモリの使い過ぎを自動で検出する方法

DT+を使ったメモリ監視による利点をもうひとつ。それはメモリ使用量があらかじめ設定したスレッシュを超えた際に自動でアラートしてくれる点です。スレッシュを超えた値がないかどうか、いちいち手動で値を確認する必要がなくなるため確認の手間がグンと減り見落としもなくなります。さらに、この機能を使うとスレッシュを超えたときのログを自動で抽出してくれるため、本当に見たいログの箇所(=どういったときにスレッシュを超えてしまったのか)をすぐに確認できます。

この設定は過去に取得したログでも適用することが可能で、同様の事象が発生していないかどうか、またアラートの原因となっている処理のパターンは同一かどうか、などの確認を行えます。

 

まとめ

このような仕組みをあらかじめ検討しておくことで、開発者各人がコード変更時やリリース時などに確認できるようになるので、予期せぬ不具合の流出を防ぐことにつながります。

また、DT+を使って解析することによって、メモリの情報だけでなく、すでに担当者がいないため情報が不足していて時間もかかるような厄介なケースでも、大きく工数をロスすることなく解析ができるようになります。短納期で高機能、開発スピードがより一層求められる昨今、デバッグ手法を磨くためのヒントとしてみなさまの参考にしていただければと思います。

【 無料でみれる! 】
動的テストツールDT+ デモ動画

ソフトウェア開発者のための動的テストツール「DT+」をご紹介する動画です。
ソースコードの実行によりログを収集し、多彩な解析機能によりソフトウェアの動作をこまかく見える化。たった数クリックの解析で、関数遷移や変数の変動、カバレッジをグラフィカルに表示します。本動画では、そんなDT+の導入手法から実際の解析の様子まで、基本的な使い方をデモンストレーションいたします。

動画で解析の様子を見る!