출처 : http://lucidtale.wordpress.com/?s=Android+Low+Memory+Killer
OOM killer & Android LMK
현재 리눅스 커널에는 OOM(Out of Memory) killer가 있다. 소스 파일 위치는 mm/oom_kill.c 이며
메모리가 부족해서 할당에 실패할 경우
alloc_pages -> … -> out_of_memory() -> select_bad_process() -> badness() 가 불리게 된다.
bad process의 선정은 다음과 같다.
/*
* The baseline for the badness score is the proportion of RAM that each
* task’s rss, pagetable and swap space use.
*/
points = get_mm_rss(p->mm) + p->mm->nr_ptes +
get_mm_counter(p->mm, MM_SWAPENTS);
/* Normalize to oom_score_adj units */
adj *= totalpages / 1000;
points += adj;
rss(Resident Set Size)는 프로세스와 관련된 physical page의 갯수라고 볼 수 있다. adj는 user에서 설정가능한 값이며 /proc/[pid]/oom_score_adj 를 통해 설정 가능하다. totalpages는 physical mem + swap mem 이라고 보면 된다.(코드를 보면 이렇게 단순하지는 않다.)
어쨋든 point가 높을수록 bad process 이며 메모리를 많이 사용하는것을 우선적으로 죽이는 것을 알 수 있다. 그 이유는 다음과 같다.
* Good in this context means that: * 1) we lose the minimum amount of work done * 2) we recover a large amount of memory * 3) we don't kill anything innocent of eating tons of memory * 4) we want to kill the minimum amount of processes (one) * 5) we try to kill the process the user expects us to kill, this * algorithm has been meticulously tuned to meet the principle * of least surprise ... (be careful when you change it) */
즉, 가장 적은 프로세스를 죽여서 가장 많은 메모리를 확보하는 것이 목적이며 user의 의견도 반영하기 위해 oom_score_adj를 설정할 수 있게 해놓았다. 이것이 커널 입장에서는 효율적이라고 볼 수는 있지만 유저의 입장에서는 그렇지 않을 수 있다. 그리고 oom_killer는 정말 메모리가 없는 마지막중에 마지막에 불리기 때문에 너무 늦은 감이 있다.(자기 자식같은 프로세스를 죽이는게 좋진 않겠지…)
그래서 안드로이드에서는 LMK(Low Memory Killer)를 따로 만들어서 유저의 입장에서 죽여도 괜찮은 프로세스를 골라 kernel oom 이 불리기전에 프로세스를 죽여 메모리를 확보한다. 그래서 안드로이드에는 kernel oom killer도 있고 Low memory killer도 있다. oom killer는 대충 이쯤 보고 다음 시간에 LMK를 집중해서 보도록 하자.
* 참고 문서 & 홈페이지
http://barriosstory.blogspot.kr/2009/02/taming-oom-killer.html
http://linux-mm.org/OOM_Killer
http://shadowxx.egloos.com/10781292
ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES : OOM을 자동으로 결정 해주는 피쳐
Android Low Memory Killer(1)
일단 Android가 LMK를 어떻게 활용하는지 그 방법을 알아보도록 하자.
Android에서
/sys/module/lowmemorykiller/parameters/adj 의 값을 읽어보면
0, 58, 117, 235, 529, 1000 <- 요렇게 나온다.
/sys/module/lowmemorykiller/parameters/minfree 의 값은 다음과 같다.
8192, 10240, 12288, 14336, 16384, 20480
위 설정값은 Activity Manager가 하게 되는데 adj 값과 minfree의 각 필드값이 짝을 이룬다. 이 값들로 종료해야 할 프로세스를 결정한다.
예를들면 현재 남은 page의 갯수가 16384개 이하(64MB)가 됐을때 adj가 529 이상인 process를 죽인다. 더 사용하다가 page갯수가 10240 이하면 이번에는 adj가 58 이하인 프로세스를 죽이게 된다. 각 app의 adj 값은 Activity Manager가 정해주게 되며 /proc/pid/oom_score_adj(또는 oom_adj)에 써주게 된다.
Activity Manager는 각 app을 다음과 같이 분류해서 adj 값을 정한다.
HIDDEN_APP_MAX_ADJ = 1000
HIDDEN_APP_MIN_ADJ = 529
BACKUP_APP_ADJ = 235
PERCEPTIBLE_APP_ADJ = 117 // ex) background music playback
VISIBLE_APP_ADJ = 58
FOREGROUND_APP_ADJ = 0
ps) oom_adj라는 것과 oom_score_adj 라는 것이 있다. oom_adj값은 과거에 쓰던 값이며 과거 버전과의 호환성을 위해 남겨놓은 값이다. 거의 의미는 같은데 oom_adj는 -16 ~ 15 사이의 값을 가지며 oom_score_adj는 -1000 ~ 1000 사이의 값을 가진다. 커널에서 먼저 바꾼듯 한데 안드로이드는 아직 oom_adj를 쓰고 있다. 그래서 안드로이드가 oom_adj에 값을 쓰면 알아서 oom_score_adj에 값이 설정된다.
참고 소스 위치(Android 4.2) : frameworks/base/services/java/com/android/server/am/ProcessList.java
Android Low Memory Killer(2)
android low memory killer의 소스는 간단하다.
LMK에서 구현한 memory shrinker 함수인 lowmem_shrink 함수를 register_shrinker() 를 통해 등록하는 것과
/sys/module/lowmemorykiller/parameters/ 에 노드를 만들어 주는 것이 전부이다.
이렇게 shrinker를 등록해놓으면 kernel에서 memory가 부족할 때 등록된 shrinker들을 불러준다.
lowmem_shrink 함수에서 하는일은 죽일 프로세스를 선정하는 일인데 일단 가장 oom_score_adj가 큰것을 찾고
만약 oom_score_adj가 같으면 rss가 더 큰 프로세스를 찾아 SIGKILL 을 날린다. shrinker 함수가 한번 불리면 여러개를
한꺼번에 죽이지는 않고 하나씩만 죽인다.
RSS(Resident set size) : the portion of a process’s memory that is held in RAM. The rest of the memory exists in swap or the filesystem(never loaded or previously unloaded parts of the executable.
즉 RSS란 순수하게 물리 메모리를 차지하고 있는 용량이다. 다음에는 RSS등과 같은 메모리 측정 관련 용어들을 알아보자
커널 소스 경로(3.4) : drivers/staging/android/lowmemorykiller.c
'Linux > Android' 카테고리의 다른 글
안드로이드 로그뷰어(LogViewer for android) - 자바 (0) | 2013.02.23 |
---|---|
[Android] OOM (Out of Memory) 와 LMK (Low Memory Killer) 이야기. (0) | 2013.02.23 |
Dalvik Optimization and Verification중에서 (dexopt) (0) | 2013.02.07 |
[Android] stop_drawing_early_suspend: timeout waiting for userspace to stop drawing (0) | 2013.01.31 |
URL 문자열 정규식으로 분리하기(JAVA) (0) | 2012.11.14 |