Androidのビルドでメモリが不足しているときのvmstat

RAMを1GBしか割り当ててないVMWare仮想マシンで、無謀にもAOSPのmasterのAndroidをビルドしてみました。

$ nohup make > make.log 2>&1 &
$ tail -F make.log

一晩かかる覚悟なので、ログアウトしても続行するように nohup をつけてバックグランドで実行しています。メモリが少ないので -j で並列化はしません。
tail -F でログをのぞいてみると

  ...
aapt: warning: string 'gsm_alphabet_default_charset' has no default translation in frameworks/base/core/res/res; found: ko
target Java: framework (out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes)
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes-jarjar.jar
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/emma_out/lib/classes-jarjar.jar
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/noproguard.classes.jar
target Dex: framework

この状態のままずっと止まっています。

別のTerminalから top コマンドで見てみると

top - 23:25:22 up 12:05,  4 users,  load average: 2.13, 2.16, 1.99
Tasks:  79 total,   1 running,  78 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.3%us,  7.2%sy,  0.0%ni,  0.0%id, 92.1%wa,  0.0%hi,  0.3%si,  0.0%st
Mem:   1019500k total,   960320k used,    59180k free,       72k buffers
Swap:  1044476k total,   719960k used,   324516k free,     1712k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND           
 3418 koba      20   0 2136m 877m    4 S  6.2 88.1   3:00.08 java               
   22 root      20   0     0    0    0 D  1.6  0.0   1:36.27 kswapd0            
    3 root      20   0     0    0    0 S  0.3  0.0   0:19.09 ksoftirqd/0        
 3682 root      20   0     0    0    0 S  0.3  0.0   0:00.02 kworker/0:2        
    1 root      20   0 24328    4    4 S  0.0  0.0   0:02.10 init               
    2 root      20   0     0    0    0 S  0.0  0.0   0:00.00 kthreadd           
    5 root      20   0     0    0    0 S  0.0  0.0   0:36.78 kworker/u:0        
    6 root      RT   0     0    0    0 S  0.0  0.0   0:00.00 migration/0        
    7 root      RT   0     0    0    0 S  0.0  0.0   1:01.59 watchdog/0         
    8 root       0 -20     0    0    0 S  0.0  0.0   0:00.00 cpuset             
    9 root       0 -20     0    0    0 S  0.0  0.0   0:00.00 khelper            
   10 root      20   0     0    0    0 S  0.0  0.0   0:00.00 kdevtmpfs          
   11 root       0 -20     0    0    0 S  0.0  0.0   0:00.00 netns              
   12 root      20   0     0    0    0 S  0.0  0.0   0:01.60 sync_supers        
   13 root      20   0     0    0    0 S  0.0  0.0   0:00.30 bdi-default        
   14 root       0 -20     0    0    0 S  0.0  0.0   0:00.00 kintegrityd        
   15 root       0 -20     0    0    0 S  0.0  0.0   0:00.00 kblockd     

たしかにjavaコマンドが大量にメモリを食っています。でも忙しいはずなのにCPUは6.2%しか使っていません、

vmstat コマンドで仮想メモリの様子をみてみると

$ vmstat 5 

これで5秒ごとに状態が表示されます。

 ...
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 1  6 725912  57464     80   7912 3123  591  3126   602  699  637  2  8  0 89
 1  6 721608  49188     88   8336 3460  450  3549   456  766  827  8 10  0 82
 0  3 721104  54832     96   8940 2667  614  2787   624  687  581  3 10  0 87
 0  2 722540  55432   1156  10936 2743 1021  3470  1026  705  609  1  8  0 91
 0  3 725296  68844     64   4112 3341 1455  3558  1478  857  686  1 10  0 90
 1  2 723764  54972     72   7856 2758  469  4718   471  675  520  1  5  0 94
 0  2 721944  57576     72   8832 3386  918  3606   922  822  801  1 10  0 89
 2  2 720836  59188     68   8836 3029  730  3038   736  744  640  1  9  0 89
 0  4 720900  58932     68   9124 3529  925  3586   927  799  694  1  9  0 90
 0  2 722444  50260     76   9088 3328 1191  3328  1194  806  704  0  6  0 93
 1  2 725964  59808     84   9100 2990 1523  2990  1533  808  665  1  7  0 92
 0  4 728740  50500     88   9124 3094 1376  3094  1378  755  547  1  5  0 94
 2  1 727372  67496     80   3292 2911  634  2911   640  719  511  1  8  0 91
 0  2 726128  52740     84   3308 3885  830  3885   833  888  676  1  6  0 93
 0  1 724024  64136     64   3784 3473  601  3738   604  858  739  1  8  0 91
 0  2 727064  56332     72   4480 3561 1790  3561  1793  809  724  1  6  0 93
 0  3 729900  71428     64   3272 3166 1695  3390  1701  823  712  1  9  0 91
 0  1 730292  62392     72   2092 3925 1518  4637  1521  909  814  1  7  0 93

si, so に注目。メモリが足りないので、swapに掃き出しと読み込みが激しく行われています。
cpuは90%以上がI/O待ち状態です。(一番右のwaの項目) これではcpuがいくら速くても意味がありません。

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND           
 3418 koba      20   0 2136m 877m    4 S  6.2 88.1   3:00.08 java       

topの画面をよく見ると、このjavaのプロセスは2136MBの仮想メモリを要求していて、割り当てられた物理メモリが877MB。実際に1024MBしかRAMは無いのでこれで全く足りず、swapという借金に手を出さざるをえない状態です。

ウィキペディアの「スラッシング」に書いてあるそのものずばりのケースです。
「物理メモリが1GBしかないのに2GBのメモリを使うアプリケーションを動かした等の場合」
スラッシング - Wikipedia

あまりにひどいので、いったん仮想マシンを止めて、RAMを2048MBに増やしてやり直しました。今度はcpuの利用率が90%以上に上がって進行するようになりました。

Androidのビルドには最低2GBのRAMは必要だとわかりました。しかし2GBでは一晩かかります。


メモリが十分にあってcpuが休み無く働いている状態は以下のページを見てください。
Androidをビルドしているときのvmstat - 組み込みの人。