一,Kernel 空间
1. @kernel-4.9\mm\Kconfig
config PROCESS_RECLAIM
bool "Enable process reclaim"
depends on PROC_FS
default n
help
It allows to reclaim pages of the process by /proc/pid/reclaim.
(echo file > /proc/PID/reclaim) reclaims file-backed pages only.
(echo anon > /proc/PID/reclaim) reclaims anonymous pages only.
(echo all > /proc/PID/reclaim) reclaims all pages.
Any other value is ignored.
2. @Vmscan.c (mm) 119815 6/4/2020
#ifdef CONFIG_PROCESS_RECLAIM
unsigned long reclaim_pages(struct list_head *page_list)
{
unsigned long dummy1, dummy2, dummy3, dummy4, dummy5;
unsigned long nr_reclaimed;
struct page *page;
unsigned long nr_isolated[2] = {0, };
struct pglist_data *pgdat = NULL;
struct scan_control sc = {
.gfp_mask = GFP_KERNEL,
.priority = DEF_PRIORITY,
.may_writepage = 1, //可以回写到文件
.may_unmap = 1, //可以解除虚拟内存映射
.may_swap = 1, //可以交换到zram
};
if (list_empty(page_list))
return 0;
list_for_each_entry(page, page_list, lru) {
ClearPageActive(page);//首先清除page的active状态
if (pgdat == NULL)
pgdat = page_pgdat(page);
/* XXX: It could be multiple node in other config */
WARN_ON_ONCE(pgdat != page_pgdat(page));
if (!page_is_file_cache(page))
nr_isolated[0]++;
else
nr_isolated[1]++;
}
mod_node_page_state(pgdat, NR_ISOLATED_ANON, nr_isolated[0]);
mod_node_page_state(pgdat, NR_ISOLATED_FILE, nr_isolated[1]);
//根据sc设置的清理参数,开始逐个清理page_list中的页
nr_reclaimed = shrink_page_list(page_list, pgdat, &sc,
TTU_UNMAP|TTU_IGNORE_ACCESS,
&dummy1, &dummy2, &dummy3, &dummy4, &dummy5, true);
while (!list_empty(page_list)) {//如果还有没有被清理完的page,则又放回到LRU缓冲列表中
page = lru_to_page(page_list);
list_del(&page->lru);
putback_lru_page(page);
}
mod_node_page_state(pgdat, NR_ISOLATED_ANON, -nr_isolated[0]);
mod_node_page_state(pgdat, NR_ISOLATED_FILE, -nr_isolated[1]);
return nr_reclaimed;
}
#endif
3. @Task_mmu.c (fs\proc) 50858 5/20/2020
#ifdef CONFIG_PROCESS_RECLAIM
enum reclaim_type {
RECLAIM_FILE,
RECLAIM_ANON,
RECLAIM_ALL,
};
//清理file cache页面, 没有回写到磁盘,而只是简单的把该页移动到 inactive file cache的LRU列表中
//为了防止回写文件造成的block? 而标准的kernel patch是需要回写到磁盘的!
static int deactivate_pte_range(pmd_t *pmd, unsigned long addr,
unsigned long end, struct mm_walk *walk)
{
pte_t *orig_pte, *pte, ptent;
spinlock_t *ptl;
struct page *page;
struct vm_area_struct *vma = walk->vma;
orig_pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
for (pte = orig_pte; addr < end; pte++, addr += PAGE_SIZE) {
ptent = *pte;
if (pte_none(ptent))
continue;
if (!pte_present(ptent))
continue;
//通过虚拟映射表,获取实际的物理页
page = vm_normal_page(vma, addr, ptent);
if (!page)
continue;
/*
* XXX: we don't handle compound page at this moment but
* it should revisit for THP page before upstream.
*/
if (PageCompound(page)) {
unsigned int order = compound_order(page);
unsigned int nr_pages = (1 << order) - 1;
addr += (nr_pages * PAGE_SIZE);
pte += nr_pages;
continue;
}
if (page_mapcount(page) > 1)
continue;
ptep_test_and_clear_young(vma, addr, pte);
test_and_clear_page_young(page);
if (PageReferenced(page))
ClearPageReferenced(page);
if (PageActive(page))
deactivate_page(page); //把该页移动到inactive list,内存不够时会被直接回收
}
pte_unmap_unlock(orig_pte, ptl);
cond_resched();
return 0;
}
//清理进程中的匿名页
static int reclaim_pte_range(pmd_t *pmd, unsigned long addr,
unsigned long end, struct mm_walk *walk)
{
pte_t *orig_pte, *pte, ptent;
spinlock_t *ptl;
LIST_HEAD(page_list);
struct page *page;
int isolated = 0;
struct vm_area_struct *vma = walk->vma;
orig_pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
for (pte = orig_pte; addr < end; pte++, addr += PAGE_SIZE) {
ptent = *pte;
if (!pte_present(ptent))
continue;
//通过虚拟映射表,获取实际的物理页
page = vm_normal_page(vma, addr, ptent);
if (!page)
continue;
/*
* XXX: we don't handle compound page at this moment but
* it should revisit for THP page before upstream.
*/
if (PageCompound(page)) {
unsigned int order = compound_order(page);
unsigned int nr_pages = (1 << order) - 1;
addr += (nr_pages * PAGE_SIZE);
pte += nr_pages;
continue;
}
if (!PageLRU(page))//判断该页是否在LRU缓存链表中,如果不是则不能回收
continue;
if (page_mapcount(page) > 1)//判断该页被映射的次数,如果多次被map也不能被回收
continue;
if (isolate_lru_page(page))
continue;
isolated++;
list_add(&page->lru, &page_list);//添加page到可以清理的page_list中
//一次最多清理32个页面,为啥?防止阻塞
if (isolated >= SWAP_CLUSTER_MAX) { //SWAP_CLUSTER_MAX = 32
pte_unmap_unlock(orig_pte, ptl);
reclaim_pages(&page_list);
isolated = 0;
pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
orig_pte = pte;
}
}
pte_unmap_unlock(orig_pte, ptl);
reclaim_pages(&page_list);//清理该进程中可以清理的匿名页
cond_resched();
return 0;
}
static ssize_t reclaim_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct task_struct *task;
char buffer[PROC_NUMBUF];
struct mm_struct *mm;
struct vm_area_struct *vma;
enum reclaim_type type;
char *type_buf;
if (!capable(CAP_SYS_NICE))
return -EPERM;
memset(buffer, 0, sizeof(buffer));
if (count > sizeof(buffer) - 1)
count = sizeof(buffer) - 1;
if (copy_from_user(buffer, buf, count))
return -EFAULT;
type_buf = strstrip(buffer);
if (!strcmp(type_buf, "file"))
type = RECLAIM_FILE;
else if (!strcmp(type_buf, "anon"))
type = RECLAIM_ANON;
else if (!strcmp(type_buf, "all"))
type = RECLAIM_ALL;
else
return -EINVAL;
task = get_proc_task(file->f_path.dentry->d_inode);//根据PID获取进程task结构
if (!task)
return -ESRCH;
mm = get_task_mm(task);//获取当前进程的内存结构
if (mm) {
struct mm_walk reclaim_walk = {
.pmd_entry = reclaim_pte_range,
.mm = mm,
};
down_read(&mm->mmap_sem);
//遍历当前进程所占用的虚拟地址
for (vma = mm->mmap; vma; vma = vma->vm_next) {
if (is_vm_hugetlb_page(vma))//没有打开CONFIG_HUGETLB_PAGE
continue;
if (vma->vm_flags & VM_LOCKED)
continue;
if (type == RECLAIM_ANON && !vma_is_anonymous(vma))
continue; //只reclaim匿名页,不是则返回
if (type == RECLAIM_FILE && vma_is_anonymous(vma))
continue; //只reclaim 文件缓存页,不是则返回
if (vma_is_anonymous(vma)) //匿名页虚拟映射
reclaim_walk.pmd_entry = reclaim_pte_range;
else //文件缓存页虚拟映射
reclaim_walk.pmd_entry = deactivate_pte_range; //MTK单独添加
//扫描虚拟内存空间, 根据设置的回调函数逐个处理
walk_page_range(vma->vm_start, vma->vm_end,
&reclaim_walk);
}
flush_tlb_mm(mm);
up_read(&mm->mmap_sem);
mmput(mm);
}
put_task_struct(task);
return count;
}
const struct file_operations proc_reclaim_operations = {
.write = reclaim_write,
.llseek = noop_llseek,
};
#endif
4. @Base.c (fs\proc) 86823 5/20/2020
static const struct pid_entry tgid_base_stuff[] = {
REG("mountinfo", S_IRUGO, proc_mountinfo_operations),
REG("mountstats", S_IRUSR, proc_mountstats_operations),
#ifdef CONFIG_PROCESS_RECLAIM
REG("reclaim", 0222, proc_reclaim_operations),
#endif
}
二, 用户Android空间
1. @OomAdjuster.java (frameworks\base\services\core\java\com\android\server\am)
/**
* Service for compacting background apps.
*/
AppCompactor mAppCompact;
OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) {
mConstants = mService.mConstants;
mAppCompact = new AppCompactor(mService);
}
void initSettings() {
mAppCompact.init();
}
private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
int changes = 0;
// don't compact during bootup,
if (mAppCompact.useCompaction() && mService.mBooted) {
// Cached and prev/home compaction
//按进程adj的值清理其内存(file cache or anon pages)!!!
//同时还收每个进程清理的间隔限制,不能清理太频繁了(导致没啥可清理的)
if (app.curAdj != app.setAdj) {
// Perform a minor compaction when a perceptible app becomes the prev/home app
// Perform a major compaction when any app enters cached
// reminder: here, setAdj is previous state, curAdj is upcoming state
if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ &&
(app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
app.curAdj == ProcessList.HOME_APP_ADJ)) {
//可感知app变成home和前一个app则清理file cache
mAppCompact.compactAppSome(app);
} else if ((app.setAdj < ProcessList.CACHED_APP_MIN_ADJ
|| app.setAdj > ProcessList.CACHED_APP_MAX_ADJ)
&& app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
&& app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
//除上条件后的app变为缓存app, 则清理file cache和压缩swap匿名页
mAppCompact.compactAppFull(app);
}
} else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE
&& app.setAdj < ProcessList.FOREGROUND_APP_ADJ
// Because these can fire independent of oom_adj/procstate changes, we need
// to throttle the actual dispatch of these requests in addition to the
// processing of the requests. As a result, there is throttling both here
// and in AppCompactor.
&& mAppCompact.shouldCompactPersistent(app, now)) {
//手机处于非全唤醒状态,则清理persistent app的file cache和压缩swap匿名页
mAppCompact.compactAppPersistent(app);
} else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE
&& app.getCurProcState()
== ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& mAppCompact.shouldCompactBFGS(app, now)) {
//手机处于非全唤醒状态,被系统调用的前台服务,则清理app的file cache和匿名页
mAppCompact.compactAppBfgs(app);
}
}
}
2. @AppCompactor.java (frameworks\base\services\core\java\com\android\server\am)
private void updateUseCompaction() {
//从哪读取的这个配置参数?????
//DeviceConfig.getBoolean ->Settings.Config.getString
->读取/data/system/users/0/settings_config.xml
mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);
if (mUseCompaction && !mCompactionThread.isAlive()) {
mCompactionThread.start();
mCompactionHandler = new MemCompactionHandler();
}
}
@GuardedBy("mAm") //表示受与mAm引用相关联的锁 保护
void compactAppSome(ProcessRecord app) {
app.reqCompactAction = COMPACT_PROCESS_SOME;//设置清理类型
mPendingCompactionProcesses.add(app);//添加到列表中,消息处理函数中再取出
mCompactionHandler.sendMessage(//发送消息异步处理
mCompactionHandler.obtainMessage(
COMPACT_PROCESS_MSG, app.setAdj, app.setProcState));
}
void compactAppFull(ProcessRecord app)
void compactAppPersistent(ProcessRecord app)
void compactAppBfgs(ProcessRecord app)
以上针对APP内存压缩处理的函数都是发消息COMPACT_PROCESS_MSG 异步处理
@GuardedBy("mAm")
void compactAllSystem() { //系统启动完成和每天半夜会调用!!!清理系统整个内存
if (mUseCompaction) {
mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage(
COMPACT_SYSTEM_MSG));
}
}
private native void compactSystem();
private final class MemCompactionHandler extends Handler {
private MemCompactionHandler() {
super(mCompactionThread.getLooper());
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case COMPACT_PROCESS_MSG: {
synchronized (mAm) {
proc = mPendingCompactionProcesses.remove(0);
pendingAction = proc.reqCompactAction;
//通过设置的compact条件,逐个判断是否需要清理该进程,
//1. 如果进程已经为PERCEPTIBLE_APP_ADJ,可感知App,则不做清理
//2. App不能清理太频繁(可以参考调试条目中的参数解释)
//compact_throttle_1=5000 如果上次是轻度清理,这次轻度compact清理两者时间间隔5秒
//compact_throttle_2=10000 如果上次是重度清理,这次轻度compact清理两者时间间隔10秒
//compact_throttle_3=500 如果上次是轻度清理,这次重度compact清理两者时间间隔0.5秒
//compact_throttle_4=10000 如果上次是重度清理,这次重度compact清理两者时间间隔10秒
//compact_throttle_5=600000 两次被系统绑定调用的前台服务重度清理间隔10分钟
//compact_throttle_6=600000 两次persisit app的重度清理间隔10分钟
//3. 确定清理action,
//compact_action_1=file 轻度compact清理,只清理file cache
//compact_action_2=all 重度compact清理,清理file cahce和匿名页zram压缩
//4. 如果清理匿名页
//compact_full_rss_throttle_kb=12000 进程占用RSS(实际使用内存包含所有共享内存)的最低清理门限为12M
//compact_full_delta_rss_throttle_kb=8000 进程被清理过,再次清理时比较上次清理完后占用的RSS和当前占用的RSS相比较,如果没有增加超出该门限8M,则不再清理
//5.以上条件都不满足,则开始清理app内存
try {
long zramFreeKbBefore = Debug.getZramFreeKb();
FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim");
fos.write(action.getBytes()); //写文件节点,触发进程内存清理
fos.close();
break;
}
case COMPACT_SYSTEM_MSG: {
compactSystem();
break;
}
}
3. @com_android_server_am_AppCompactor.cpp (frameworks\base\services\core\jni)
// This performs per-process reclaim on all processes belonging to non-app UIDs.
// For the most part, these are non-zygote processes like Treble HALs, but it
// also includes zygote-derived processes that run in system UIDs, like bluetooth
// or potentially some mainline modules. The only process that should definitely
// not be compacted is system_server, since compacting system_server around the
// time of BOOT_COMPLETE could result in perceptible issues.
static void com_android_server_am_AppCompactor_compactSystem(JNIEnv *, jobject) {
std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir);
struct dirent* current;
//遍历proc/节点,获取进程信息,这将是一个耗时的过程
while ((current = readdir(proc.get()))) {
if (current->d_type != DT_DIR) {
continue;
}
// don't compact system_server, rely on persistent compaction during screen off
// in order to avoid mmap_sem-related stalls
if (atoi(current->d_name) == getpid()) { //不清理自身system_server的内存
continue;
}
std::string status_name = StringPrintf("/proc/%s/status", current->d_name);
struct stat status_info;
if (stat(status_name.c_str(), &status_info) != 0) {
// must be some other directory that isn't a pid
continue;
}
// android.os.Process.FIRST_APPLICATION_UID
if (status_info.st_uid >= 10000) {//只清理非用户进程,比如系统进程和Native进程
continue;
}
std::string reclaim_path = StringPrintf("/proc/%s/reclaim", current->d_name);
WriteStringToFile(std::string("all"), reclaim_path);
}
}
4. ActivityManagerService.java (frameworks\base\services\core\java\com\android\server\am)
final void finishBooting() {
mUserController.sendBootCompleted(
new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
boolean sticky, int sendingUser) {
synchronized (ActivityManagerService.this) {
//系统启动完成后开始清理系统进程
mOomAdjuster.mAppCompact.compactAllSystem();
requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
}
}
});
}
@Override
public void performIdleMaintenance() {
synchronized (this) {
// Compact all non-zygote processes to freshen up the page cache.
mOomAdjuster.mAppCompact.compactAllSystem();
}
5. @StorageManagerService.java (frameworks\base\services\core\java\com\android\server)
private void handleSystemReady() {
// Start scheduling nominally-daily fstrim operations
MountServiceIdler.scheduleIdlePass(mContext);
}
6. @MountServiceIdler.java (frameworks\base\services\core\java\com\android\server)
public static void scheduleIdlePass(Context context) {
JobScheduler tm = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
Calendar calendar = tomorrowMidnight();
final long timeToMidnight = calendar.getTimeInMillis() - System.currentTimeMillis();
//创建job, 触发条件是系统IDLE, 充电,时间为第二天半夜3点, 只运行一次
JobInfo.Builder builder = new JobInfo.Builder(MOUNT_JOB_ID, sIdleService);
builder.setRequiresDeviceIdle(true);
builder.setRequiresCharging(true);
builder.setMinimumLatency(timeToMidnight);
tm.schedule(builder.build());
}
public boolean onStartJob(JobParameters params) {
// First have the activity manager do its idle maintenance. (Yes this job
// is really more than just mount, some day it should be renamed to be system
// idleer).
try {
ActivityManager.getService().performIdleMaintenance();
} catch (RemoteException e) {
}
StorageManagerService ms = StorageManagerService.sSelf;
if (ms != null) {
synchronized (mFinishCallback) {
mStarted = true;
}
ms.runIdleMaint(mFinishCallback);
}
}
private Runnable mFinishCallback = new Runnable() {
@Override
public void run() {
Slog.i(TAG, "Got mount service completion callback");
synchronized (mFinishCallback) {
if (mStarted) {
jobFinished(mJobParams, false);
mStarted = false;
}
}
// ... and try again tomorrow, 重新创建job
scheduleIdlePass(MountServiceIdler.this);
}
};
三, 调试
1. #dumpsys activity settings
AppCompactor settings
use_compaction=true 表示使用单个进程内存清理功能
compact_action_1=file 轻度compact清理,只清理file cache
compact_action_2=all 重度compact清理,清理file cahce和匿名页zram压缩
compact_throttle_1=5000 如果上次是轻度清理,这次轻度compact清理两者时间间隔5秒
compact_throttle_2=10000 如果上次是重度清理,这次轻度compact清理两者时间间隔10秒
compact_throttle_3=500 如果上次是轻度清理,这次重度compact清理两者时间间隔0.5秒
compact_throttle_4=10000 如果上次是重度清理,这次重度compact清理两者时间间隔10秒
compact_throttle_5=600000 两次被系统绑定调用的前台服务重度清理间隔10分钟
compact_throttle_6=600000 两次persisit app的重度清理间隔10分钟
compact_statsd_sample_rate=0.1 随机更新compact事件给statsd的采样间隔
compact_full_rss_throttle_kb=12000 进程占用RSS(实际使用内存包含所有共享内存)的最低清理门限为12M
compact_full_delta_rss_throttle_kb=8000 进程被清理过,再次清理时比较上次清理完后占用的RSS和当前占用的RSS相比较,如果没有增加超出该门限8M,则不再清理
compact_proc_state_throttle=[12] 当进程状态为12(PROCESS_STATE_RECEIVER)时,则不做清理。清理排除的进程状态
31 some, 104 full, 79 persistent, 35 BFGS compactions. //记录已经被内存清理处理过的次数, some表示轻度清理,full表示重度清理
Tracking last compaction stats for 40 processes.
MTBF 跑完后:
7695 some, 45640 full, 927 persistent, 379 BFGS compactions.
Tracking last compaction stats for 96 processes.
2. #logcat -b events -s am_compact
06-10 14:13:55.085 1059 1159 I am_compact: [32436,com.google.android.gms,all,121800,101540,18792,14004,-7768,0,-7768,7768,159,2,448136726,1001,20,471428,-7768]
数据具体说明:
com.google.android.gms表示被清理的进程名称
all 表示清理操作的类型, file是只清理file cache, all是清理file cache和匿名页
121800,101540,18792,14004 表示清理前进程的4个RSS值
-7768,0,-7768,7768 表示清理后进程这4个RSS的差值,负数表示减少,正数表示增加
159 清理操作消耗的时间,单位为ms
2 表示清理进程类型 1:filecache清理;2: filecache&匿名页清理; 3: persistent进程清理; 4:前台服务清理
448136726 上一次进程清理时的系统时间
1001 清理前进程的adj
20 清理前的进程状态 PROCESS_STATE_CACHED_EMPTY=20 @ActivityManager.java
ActivityManager.java (frameworks\base\core\java\android\app) 166084 5/20/2020
471428 进程清理前free zram的大小
-7768 进程清理后free zram的变化值, 负数表示减少,正数表示增加
06-10 14:32:51.786 1059 1159 I am_compact: [30930,com.facebook.appmanager,all,75452,60612,14228,14524,-3464,0,-3464,3464,81,2,448172391,1001,20,467628,-3452]
06-10 15:07:24.910 1059 1159 I am_compact: [26856,android.process.acore,file,65492,47008,18164,5860,0,0,0,0,8,1,445391394,0,6,502496,0]
06-10 15:07:25.462 1059 1159 I am_compact: [29866,com.google.android.apps.photos,all,114724,99648,13964,30692,-1924,0,-1924,1924,50,2,439030180,1001,19,505044,-720]
06-10 15:07:25.990 1059 1159 I am_compact: [24686,com.google.process.gservices,file,62472,46840,15348,6092,0,0,0,0,9,0,0,100,6,516600,0]
06-10 15:07:26.771 1059 1159 I am_compact: [24822,com.google.android.partnersetup,all,57540,41448,15808,6160,-2268,0,-2268,2268,48,0,0,1001,20,516796,-1832]
3. 如何通过修改config配置
DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);
-->DeviceConfig.getBoolean("activity_manager", "use_compaction", true)
在/data/system/users/0/settings_config.xml中添加其定义
<settings version="-1">
<setting id="0" name="connectivity/captive_portal_use_https" value="0" package="android" defaultValue="0" defaultSysSet="true" tag="null" />
<setting id="1" name="activity_manager/use_compaction" value="0" package="android" defaultValue="0" defaultSysSet="true" />
</settings>
四,优化思路
1. 清理compactAllSystem是否频次可以更密点? 现在是开机结束和每天半夜充电且待机的情况下 才清理
2. 已经处于cache的进程是否有必要做内存清理? 因为很快会被kill掉
3. compactAllSystem只清理非用户进程,比如系统进程和Native进程,另外system_server不做清理
4. process reclaim是以进程为单位触发文件页,匿名页的内存优化策略。上层AMS根据adj的变化提供优化参数
- 可感知app变成home和前一个app则清理file cache, 不清理匿名页,随时准备快速切换运行
- 除上条件后的app变为缓存app, 则清理file cache和压缩swap匿名页。最大限度减少内存占用
- 手机处于非全唤醒状态,则清理persistent app的file cache和压缩swap匿名页
- 手机处于非全唤醒状态,被系统调用的前台服务,则清理app的file cache和匿名页
5. MTK对file cache页的清理做了优化,没有按照标准的kernel patch对匿名页和文件缓存页都做回收处理。而是简单的把active file cache LRU列表中的页移动到inactive file cache LRU.
6. 单个进程的内存清理,触发条件是否可以更激进些? 而不只是仅清理处于cache状态下的进程
7. 单个进程的内存清理,难道没有需要只清理匿名页的情况?
参考
https://www.jianshu.com/p/ae4ca096201a
https://lore.kernel.org/patchwork/patch/688100/ linux kernel patch for process reclaim