---
title: 'Android - Low Memory Killer'
disqus: kyleAlien
---
Android - Low Memory Killer
===
## OverView of Content
[TOC]
## 概述
手機的設備,由於內存有限,若沒有處理好內存也會導致 OOM (Out Of Memory)
但是許多的程序都會有緩存機制(eg. Activity#finish 也不會立刻清除),這種機制是可以加快程序的啟動,減少反覆創建,不過相對來說就會一直佔用著內存
## 內存回收
### Linux - OOMKiller
* Linux 內核有自己的監控機制,就是 **OOMKiller**,在系統塊到達內存臨界點時 這個 OOM 的管理會自動跳出來清理背景程序(依照策不同會回收不同對象的記憶體)
* 一般來說會依照優先順序,從最不重要的進程開始殺,當然也會考慮到以下幾點
1. 進程消耗的內存
2. 進程佔用的 CPU 時間
3. oom_adj (OOM 權重)
* Linux 可以透過 **[/proc(映射內存產生的數據,並不是真正資料)](https://hackmd.io/MtOap5UaR7KcJpdTisc75g?view#%E8%B3%87%E6%96%99%E5%A4%BE%E4%BB%8B%E7%B4%B9)** 資料夾查看對應的 oom_adj
1. htop 查看 目前的程序的資訊,主要是要查 PID
> 
2. 接著查看 `/proc/<PID\>/oom_adj` & `/proc/<PID\>/oom_score` (score 越低則越晚被回收資源),下圖則代表 PID 為 8270 的程序不容易被回收
> 
### Android - Low Memory Killer (LMK)
* Android 系統也是基於 Linux's OOMKiller 的核心思想,實現了**不同階級的 Killer**
* Source code 在 `/ drivers / staging / android / lowmemorykiller.c`,在初始化時會註冊一個 shrinker 回調,**當記憶體到達臨界點時就會呼叫 lowmem_shrinker**
```c=
// lowmemorykiller.c
// 當記憶體到達臨界點時就會呼叫 lowmem_shrinker
static struct shrinker lowmem_shrinker = {
.shrink = lowmem_shrink,
.seeks = DEFAULT_SEEKS * 16
};
static int __init lowmem_init(void)
{
register_shrinker(&lowmem_shrinker); // 註冊監聽
return 0;
}
```
* 等級 `lowmem_adj` & 閥值 `lowmem_minfree`,**兩者是相互對應的**,Eg. 當系統剩下 16 M 記憶體時,就會清理調 lowmem_adj 為 6 以上的進程(adj 的取值範圍是 -17~15,數字越小代表該進程的優先級別越高,越晚被回收)
```c=
static int lowmem_adj[6] = { // 默認 4 個
0,
1,
6,
12,
};
static int lowmem_adj_size = 4; // 單位大小
static int lowmem_minfree[6] = {
3 * 512, /* 6MB */ // 512 * 3 * 4 = 6144 KB
2 * 1024, /* 8MB */
4 * 1024, /* 16MB */
16 * 1024, /* 64MB */
};
```
> 
* 默認情況下,adj 會分為以下幾種
| adj 值 | 描述 | 說明 |
| -------- | -------- | -------- |
| 15 | HIDDEN_APP_MAX_AD | 當前運行不可見的 aCTIVITY 進程 |
| 9 | HIDDEN_APP_MIN_ADJ | 同上 |
| 8 | SERVICE_B_ADJ | B list of srvice,與 A list 相比,他們對用戶的黏合更低 |
| 7 | PREVIOUS_APP_ADJ | 與用戶前一次互交的進程,也就上次一次使用的程序 |
| 6 | HOME_APP_ADJ | Launch 進程(桌面程序) |
| 5 | SERVCIE_ADJ | 當前運行了 Application service 進程 |
| 4 | BACK_UP_ADJ | 專門於 backup 相關操作的進程 |
| 3 | HEAVY_WEIGHT_APP_ADJ | 重量級程序 |
| 2 | PERCEPTIBLE_APP_ADJ | 用戶可以感覺到,但是不可見到(後台運行)Eg. 音樂播放 |
| 1 | VISIBLE_APP_ADJ | 前台可見的 Activity 進程 |
| 0 | FOREGROUND_APP_ADJ | 當前正在使用(前台運行)的進程 |
| -12 | PERSISTENT_PROC_ADJ | Persistent 進程,Eg. telephony |
| -16 | SYTEM_ADJ | 系統進程 |
### 調整 Android LMK 閥值
* 上面有說到 **`lowmem_adj`、`owmem_adj_size` 只是系統預設的數值,我們可以根據須修下去修改**
1. Android 系統提供了對應的文件讓我們修改
```shell=
/sys/module/lowmemorykiller/parameters/adj
/sys/module/lowmemorykiller/parameters/minfree
```
接下來可以在 **init.rc** 中加入設定
```shell=
# 定義 0 & 8
/sys/module/lowmemorykiller/parameters/adj 0, 8
# 閥值 1M & 4M
/sys/module/lowmemorykiller/parameters/minfree 1024, 4096
```
2. ActivityManagerService (AMS) 有一個 updateOomLevels 的函數,它的內部實現原理也是通過寫上面兩個文件來實現的
:::success
* AMS 在運行時也會根據系統當前配置自動調整 adj & minfree 來適配不同的硬體
:::
3. AndroidMainfest 文件:添加 persistent=true 的屬性(要注意可能會引發系統無法回收記憶體的情況)
## Appendix & FAQ
:::info
:::
###### tags: `Android 系統`