Life with IT

山好きITエンジニア 木檜(こぐれ)和明 による発信の場

C

2016/10/30更新

対応バージョン: gcc-5.4.0

プログラムがメモリを大量に確保するとスワップまで全て使い果たしてシステムが極端に重くなるが、その過程において同じ実行環境にいる全てのプロセスが影響を受けるので、例えばサーバプロセスがクライアントからのリクエストに応答できずにタイムアウトした時の挙動を確かめたいという要件があった時のためにメモリを大量に確保するプログラムを書いた。

処理は単純で、一回あたり100MBのメモリをmalloc()で確保してそれを引数で指定した回数分繰り返すだけである。

尚、引数チェックなどのエラー処理は省略している。

memfull.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int main(int argc, char *argv[])
{
  int size_mb = 100;                    /* mallocサイズ(100MB) */
  int size    = 1024 * 1024 * size_mb;  /* mallocサイズ計算(単位:Bytes) */
  int times   = atoi(argv[1]);          /* malloc回数 */
                                        /* 例) 500000回だと100MBx500000=50TB */

  char *str;                            /* malloc()で確保した領域 */
  int i;

  printf("start\n");

  for(i = 0; i < times; i++)            /* 引数で指定した回数分malloc() */
  {
    str = (char *)malloc(size);


    if (str == NULL)                    /* malloc()失敗 */
    {
      printf("malloc() error: %s\n", strerror(errno));
      exit(EXIT_FAILURE);
    }

    printf("malloc(%07d/%07d)\n", i, times);
  }

  printf("end\n");

  return(EXIT_SUCCESS);
}

このプログラムをコンパイルして実行し、swaponコマンドとvmstatコマンドでメモリの使用量を調べながらプロセスがスワップを使い果たすまでの経過を見る。この間に他のプロセスの挙動を調べる等を行うとよい。

尚、スワップを使い果たすことにより全てのプロセスが想定外の動作をする可能性があるのでテストは他に影響を与えないマシンで行うこと。

% gcc -o memfull memfull.c

まずは5回でテスト。100MB x 5 = 500MB分のメモリを確保するだけなので一瞬で終了し、スワップを使うこともない。

% ./memfull 5
start
malloc(0000000/0000005)
malloc(0000001/0000005)
malloc(0000002/0000005)
malloc(0000003/0000005)
malloc(0000004/0000005)
end

続いてソースの例にあるような500,000回でテストし、100MB x 500,000 = 50TB分のメモリを確保しにいく。ただし実際に確保されるサイズはもっと少ない。

テスト前にそれぞれ別ターミナルでswaponコマンドとvmstatコマンドでメモリの使用量を監視しておく。この例では4GBのうち0.3GB使用している。

swapon -sコマンドを1秒おきに実行
% watch -n 1 swapon -s
Every 1.0s: swapon -s                   Sun Oct 30 10:32:43 2016

Filename                Type            Size    Used    Priority
/dev/mmcblk0p3          partition       4084732 320264  -1
:
vmstatコマンドを1秒おきに実行
% vmstat 1
procs -----------memory---------- -swap- --io-- -system- ------cpu-----
 r  b   swpd    free  buff  cache si  so bi  bo  in   cs us sy id wa st
 0  0 320264 1063388 14368 168388  0   1 14  21  27   46  1  0 99  0  0
 0  0 320264 1063404 14368 168388  0   0  0   0 109  182  0  0 99  0  0
 0  0 320264 1063404 14368 168388  0   0  0   0  94  162  0  1 99  0  0
:

監視を仕込んだらテストを行う。

% ./memfull 500000
start
malloc(0000001/0500000)
malloc(0000002/0500000)
malloc(0000003/0500000)
:
malloc(0421942/0500000)
malloc(0421943/0500000)
malloc(0421944/0500000)
malloc() error: Cannot allocate memory

42万回を越えたところでメモリ確保に失敗してプログラムが終了している。この時swaponとvmstatの状態は以下のようになっている。

swapon
Filename                Type            Size    Used    Priority
/dev/mmcblk0p3          partition       4084732 4084720 -1

(テスト開始前)
Filename                Type            Size    Used    Priority
/dev/mmcblk0p3          partition       4084732 320264  -1

テスト開始前と比べてスワップを使い果たしているのが分かる。

vmstat
procs ----------memory---------- ---swap--- ----io----- -system-- ------cpu-----
 r  b   swpd    free buff  cache   si    so    bi    bo   in   cs us sy id wa st
 2  0 3948364 143372 3300  95744    0 85140     0 85136 3792 5269  1 10 73 16  0
 2  3 4032828 146600 3444  98064  656 84844  2784 84848 6559 8805  1 13 44 42  0
 1  2 4084720 131896 3720 105384 2528 54360 10204 54360 6561 9988  2 16 37 45  0

(テスト開始前)
procs ----------memory---------- ---swap--- ----io----- -system-- ------cpu-----
 r  b   swpd    free buff  cache   si    so    bi    bo   in   cs us sy id wa st
 0  0 320264 1063388 14368 168388   0     1    14    21   27   46  1  0 99  0  0
 0  0 320264 1063404 14368 168388   0     0     0     0  109  182  0  0 99  0  0
 0  0 320264 1063404 14368 168388   0     0     0     0   94  162  0  1 99  0  0

テスト開始前と比べて以下の違いがあるのが分かる。

memory

メモリ使用量(swpd)と空き容量(free)が逆転

swap

スワップアウト(so)とブロックデバイスへの出力(bo)が頻繁に発生

system

割り込み(in)とコンテキストスイッチ(cs)が増加

cpu

カーネルモードの処理(sy)とI/O待ち(wa)が発生

このようにスワップを使い果たすほどメモリを大量に確保するとシステムの挙動が通常と大きく変わるのでテストをする場合は十分に注意して行うこと。