2011/10/03

LowMemoryKillerによりkillされる閾値について


アプリがLowMemoryKillerによってkillされる閾値について調査しました。

LowMemoryKillerは端末上メモリの空き容量が少なくなると、容量確保のため、
決まったルールに従ってアプリをkill(強制終了)する機構です。

LowMemoryでkillされる閾値(メモリの空き容量)は固定ではなくアプリの状態で
上下します。
閾値は下記より求めることができます。

(1)閾値の算出
Androidのプロパティには下記が定義されている。
(実際にはinit.rcで設定される)
[ro.FOREGROUND_APP_ADJ]: [0]
[ro.VISIBLE_APP_ADJ]: [1]
[ro.HOME_APP_ADJ]: [6]
[ro.HIDDEN_APP_MIN_ADJ]: [7]
[ro.EMPTY_APP_ADJ]: [15]

[ro.FOREGROUND_APP_MEM]: [2048]
[ro.VISIBLE_APP_MEM]: [3072]
[ro.HOME_APP_MEM]: [6144]
[ro.HIDDEN_APP_MEM]: [7168]
[ro.EMPTY_APP_MEM]: [8192]

[XXXX_XXX_ADJ]は「システム内で生かすべき優先順位」です。
より値が小さいほどシステムからkillされづらくなります。
つまりは、値が大きければ閾値がよりシビアであり、値が小さければその逆と
なります。

XXXX_XXXにあたる部分はそのアプリの状態を表します。
状態の詳細は下記のコードコメントから予想できると思います。
// This is the process running the current foreground app.  We'd really
// rather not kill it! Value set in system/rootdir/init.rc on startup.
final int FOREGROUND_APP_ADJ;


// This is a process only hosting activities that are visible to the
// user, so we'd prefer they don't disappear. Value set in
// system/rootdir/init.rc on startup.
final int VISIBLE_APP_ADJ;

// This is a process holding the home application -- we want to try
// avoiding killing it, even if it would normally be in the background,
// because the user interacts with it so much.
final int HOME_APP_ADJ;

// This is a process only hosting activities that are not visible,
// so it can be killed without any disruption. Value set in
// system/rootdir/init.rc on startup.
final int HIDDEN_APP_MAX_ADJ;
static int HIDDEN_APP_MIN_ADJ;

// This is a process without anything currently running in it.  Definitely
// the first to go! Value set in system/rootdir/init.rc on startup.
// This value is initalized in the constructor, careful when refering to
// this static variable externally.
static int EMPTY_APP_ADJ;


ここでもう一度プロパティを見てみると、アプリのどの状態がkillされづらい
(値が小さい)かがわかります。
フォアグラウンドであればよりkillされづらく、背面にいればkillされやすい
ようです。
また、HOMEアプリが特別扱いされているのもわかります。


次に、killされる閾値について見ていきます。
[XXXX_XXX_MEM]はアプリ動作に最低限必要な空きページ数です。
この値を下回るとアプリはkillされることになります。

この値はページ数であり、最低限必要な空きメモリ量ではないことに注意してください。
LowMemoryでkillされる閾値(空きメモリ量)は下記で求めることができます。
ページ数([XXXX_XXX_MEM]) × ブロックサイズ(1ページあたりのサイズ)

ブロックサイズは各端末により異なります。
下記のシェルコマンドで端末のブロックサイズを求めることができます。
# df /system

実行結果)
Filesystem             Size   Used   Free   Blksize(ブロックサイズ)
/system                 86M    86M     0K   4096

例えば、[ro.FOREGROUND_APP_MEM]: [2048]でブロックサイズが4096の場合、
2048(ページ数)×4096(ブロックサイズ)=8388608Byte
となり、killされる閾値は約8MBということになります。

下記まとめとなります。
※Blksizeやページ数を上記とした場合

アプリの状態 = 閾値
FOREGROUND = 約8MB
VISIBLE = 約12MB
HOME = 約25MB
HIDDEN = 約29MB
EMPTY = 約33MB

HOMEアプリは空きメモリ容量約25MBを下回るとkillされるのに対して、
FOREGROUNDなアプリは空きメモリ容量約8MBを下回らないとkillされない。

以上です。