原创文章,谢绝转载!
AndroID Q新增了部分系统性能优化方案,这里简单学习下,本篇文章先分析app compaction。
一、愿景:
在保证后台进程尽量不被杀的基础上减少它们的内存占用。
二、思路:
AMS与Kernel层联动对满足一定条件的App进行内存压缩。
Google官方样例数据:占用1.8G内存的游戏,压缩后只占700M。该功能在高端机上没有明显的卡顿和延迟。
三、源码分析
AndroidQ上,AMS中引入了OomAdjuster来统一管理oomadj相关逻辑。
frameworks/base/services/core/java/com/androID/server/am/ActivityManagerService.java
public ActivityManagerService(Injector injector, ServiceThread handlerThread) { ... mOomAdjuster = new OomAdjuster(this, mProcessList, activeUIDs); ...}
在AMS构造方法中实例化OomAdjuster
OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUIDs activeUIDs) { ... mAppCompact = new AppCompactor(mService); ...}
在OomAdjuster构造方法中实例化AppCompactor
voID initSettings() { mAppCompact.init();}
初始化mAppCompact
public final voID installSystemProvIDers() { ... mOomAdjuster.initSettings(); ...}
接下来看看AppCompactor这个核心类
frameworks/base/services/core/java/com/androID/server/am/AppCompactor.java
public AppCompactor(ActivityManagerService am) { mAm = am; mCompactionThread = new ServiceThread("CompactionThread", THREAD_PRIORITY_FOREGROUND, true); mProcStateThrottle = new HashSet<>();}
创建了一个loop线程,并且优先级还挺高。
/*** Reads phenotype config to determine whether app compaction is enabled or not and* starts the background thread if necessary.*/public voID init() { DeviceConfig.addOnPropertIEsChangedListener(DeviceConfig.nameSPACE_ACTIVITY_MANAGER, ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener); synchronized (mPhenotypeFlagLock) { updateUseCompaction(); updateCompactionActions(); updateCompactionThrottles(); updateStatsdSampleRate(); updateFullRSSThrottle(); updateFullDeltaRSSThrottle(); updateProcStateThrottle(); } Process.setThreadGroupAndcpuset(mCompactionThread.getThreadID(), Process.THREAD_GROUP_SYstem);}
做了一些初始化 *** 作,例如::
/*** Reads the flag value from DeviceConfig to determine whether app compaction* should be enabled, and starts the compaction thread if needed.*/@GuardedBy("mPhenotypeFlagLock")private voID updateUseCompaction() { mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.nameSPACE_ACTIVITY_MANAGER, KEY_USE_COMPACTION, DEFAulT_USE_COMPACTION); if (mUseCompaction && !mCompactionThread.isAlive()) { mCompactionThread.start(); mCompactionHandler = new MemCompactionHandler(); }}
决定是否做压缩, 默认该功能没有打开。
下面来看看FW的核心压缩逻辑:
调用点在OomAdjuster的applyOomAdjLocked:
private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingall, long Now, long NowElapsed) {... //DeviceConfig配置决定它做压缩 同时系统已经完成bootingif (mAppCompact.useCompaction() && mService.mBooted) { // Cached and prev/home compaction if (app.curAdj != app.setAdj) {//当前adj有变化 // 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)) { [mAppCompact.compactAppSome(app);](http://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) { [mAppCompact.compactAppFull(app);](http://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)) { [mAppCompact.compactAppPersistent(app);](http://mAppCompact.compactAppPersistent(app);) } else if (mService.mWakefulness != PowerManagerInternal.WAKEFulnesS_AWAKE && app.getCurProcState() == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE && mAppCompact.shouldCompactBFGS(app, Now)) { [mAppCompact.compactAppBfgs(app);](http://mAppCompact.compactAppBfgs(app);) }}...}
逻辑总结:
可感知进程变为PREVIoUS(700)或HOME(600)进程,则执行compactAppSome,做一次minor compaction。
非cache进程进入到cache区间:CACHE_MAX(999)-CACHE_MIN(900)之间,则执行compactAppFull,做一次major compaction。
当前手机是非awake状态且进程优先级高于前台,并且满足shouldCompactPersistent逻辑:即当前进程从来没压缩过,或者距离上次压缩时间>10min.则执行compactAppPersistent。
当前手机是非awake状态且进程有前台服务,并且满足shouldCompactBFGS逻辑,与shouldCompactPersistent一致。则执行compactAppBfgs。
对应的这几个方法:
@GuardedBy("mAm")voID compactAppSome(ProcessRecord app) { app.reqCompactAction = COMPACT_PROCESS_SOME; mPendingCompactionProcesses.add(app); mCompactionHandler.sendMessage( mCompactionHandler.obtainMessage( COMPACT_PROCESS_MSG, app.setAdj, app.setProcState));}@GuardedBy("mAm")voID compactAppFull(ProcessRecord app) { app.reqCompactAction = COMPACT_PROCESS_FulL; mPendingCompactionProcesses.add(app); mCompactionHandler.sendMessage( mCompactionHandler.obtainMessage( COMPACT_PROCESS_MSG, app.setAdj, app.setProcState));}@GuardedBy("mAm")voID compactAppPersistent(ProcessRecord app) { app.reqCompactAction = COMPACT_PROCESS_PERSISTENT; mPendingCompactionProcesses.add(app); mCompactionHandler.sendMessage( mCompactionHandler.obtainMessage( COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));}@GuardedBy("mAm")voID compactAppBfgs(ProcessRecord app) { app.reqCompactAction = COMPACT_PROCESS_BFGS; mPendingCompactionProcesses.add(app); mCompactionHandler.sendMessage( mCompactionHandler.obtainMessage( COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));}
另外这里还有一个方法需要关注下,它的调用在AMS的finishBooting()
@GuardedBy("mAm")voID compactAllSystem() { if (mUseCompaction) { mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage( COMPACT_SYstem_MSG)); }}
发送消息
private final class MemCompactionHandler extends Handler { private MemCompactionHandler() { super(mCompactionThread.getLooper()); } @OverrIDe public voID handleMessage(Message msg) { switch (msg.what) { case COMPACT_PROCESS_MSG: { long start = SystemClock.uptimeMillis(); ProcessRecord proc; int pID; String action; final String name; int pendingAction, lastCompactAction; long lastCompactTime; LastCompactionStats lastCompactionStats; int lastOomAdj = msg.arg1; int procState = msg.arg2; synchronized (mAm) { proc = mPendingCompactionProcesses.remove(0); pendingAction = proc.reqCompactAction; pID = proc.pID; name = proc.processname; // don't compact if the process has returned to perceptible // and this is only a cached/home/prev compaction if ((pendingAction == COMPACT_PROCESS_SOME || pendingAction == COMPACT_PROCESS_FulL) && (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ)) { if (DEBUG_COMPACTION) { Slog.d(TAG_AM, "SkipPing compaction as process " + name + " is " + "Now perceptible."); } return; } lastCompactAction = proc.lastCompactAction; lastCompactTime = proc.lastCompactTime; // remove rather than get so that insertion order will be updated when we // put the post-compaction stats back into the map. lastCompactionStats = mLastCompactionStats.remove(pID); } if (pID == 0) { // not a real process, either one being launched or one being killed return; } // basic throttling // use the Phenotype flag knobs to determine whether current/prevous // compaction combo should be throtted or not // Note that we explicitly don't take mPhenotypeFlagLock here as the flags // should very seldom change, and taking the risk of using the wrong action is // preferable to taking the lock for every single compaction action. if (lastCompactTime != 0) { if (pendingAction == COMPACT_PROCESS_SOME) { if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < mCompactThrottleSomeSome)) || (lastCompactAction == COMPACT_PROCESS_FulL && (start - lastCompactTime < mCompactThrottleSomeFull))) { if (DEBUG_COMPACTION) { Slog.d(TAG_AM, "SkipPing some compaction for " + name + ": too soon. throttle=" + mCompactThrottleSomeSome + "/" + mCompactThrottleSomeFull + " last=" + (start - lastCompactTime) + "ms ago"); } return; } } else if (pendingAction == COMPACT_PROCESS_FulL) { if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < mCompactThrottleFullSome)) || (lastCompactAction == COMPACT_PROCESS_FulL && (start - lastCompactTime < mCompactThrottleFullFull))) { if (DEBUG_COMPACTION) { Slog.d(TAG_AM, "SkipPing full compaction for " + name + ": too soon. throttle=" + mCompactThrottleFullSome + "/" + mCompactThrottleFullFull + " last=" + (start - lastCompactTime) + "ms ago"); } return; } } else if (pendingAction == COMPACT_PROCESS_PERSISTENT) { if (start - lastCompactTime < mCompactThrottlePersistent) { if (DEBUG_COMPACTION) { Slog.d(TAG_AM, "SkipPing persistent compaction for " + name + ": too soon. throttle=" + mCompactThrottlePersistent + " last=" + (start - lastCompactTime) + "ms ago"); } return; } } else if (pendingAction == COMPACT_PROCESS_BFGS) { if (start - lastCompactTime < mCompactThrottleBFGS) { if (DEBUG_COMPACTION) { Slog.d(TAG_AM, "SkipPing bfgs compaction for " + name + ": too soon. throttle=" + mCompactThrottleBFGS + " last=" + (start - lastCompactTime) + "ms ago"); } return; } } } //这里action最终只分为了两种: //some对应"file" //full、presistent、bfgs都对应"all" switch (pendingAction) { case COMPACT_PROCESS_SOME: action = mCompactActionSome; break; // For the time being, treat these as equivalent. case COMPACT_PROCESS_FulL: case COMPACT_PROCESS_PERSISTENT: case COMPACT_PROCESS_BFGS: action = mCompactActionFull; break; default: action = COMPACT_ACTION_NONE; break; } if (COMPACT_ACTION_NONE.equals(action)) { return; } if (mProcStateThrottle.contains(procState)) { if (DEBUG_COMPACTION) { Slog.d(TAG_AM, "SkipPing full compaction for process " + name + "; proc state is " + procState); } return; } long[] RSSBefore = Process.getRSS(pID); long anonRSSBefore = RSSBefore[2]; if (RSSBefore[0] == 0 && RSSBefore[1] == 0 && RSSBefore[2] == 0 && RSSBefore[3] == 0) { if (DEBUG_COMPACTION) { Slog.d(TAG_AM, "SkipPing compaction for" + "process " + pID + " with no memory usage. Dead?"); } return; } if (action.equals(COMPACT_ACTION_FulL) || action.equals(COMPACT_ACTION_ANON)) { if (mFullAnonRSSThrottleKb > 0L && anonRSSBefore < mFullAnonRSSThrottleKb) { if (DEBUG_COMPACTION) { Slog.d(TAG_AM, "SkipPing full compaction for process " + name + "; anon RSS is too small: " + anonRSSBefore + "KB."); } return; } if (lastCompactionStats != null && mFullDeltaRSSThrottleKb > 0L) { long[] lastRSS = lastCompactionStats.getRSSAfterCompaction(); long absDelta = Math.abs(RSSBefore[1] - lastRSS[1]) + Math.abs(RSSBefore[2] - lastRSS[2]) + Math.abs(RSSBefore[3] - lastRSS[3]); if (absDelta <= mFullDeltaRSSThrottleKb) { if (DEBUG_COMPACTION) { Slog.d(TAG_AM, "SkipPing full compaction for process " + name + "; abs delta is too small: " + absDelta + "KB."); } return; } } } // Now we've passed through all the throttles and are going to compact, update // bookkeePing. switch (pendingAction) { case COMPACT_PROCESS_SOME: mSomeCompactionCount++; break; case COMPACT_PROCESS_FulL: mFullCompactionCount++; break; case COMPACT_PROCESS_PERSISTENT: mPersistentCompactionCount++; break; case COMPACT_PROCESS_BFGS: mBfgsCompactionCount++; break; default: break; } try { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact " + ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full") + ": " + name); long zramFreeKbBefore = DeBUG.getZramFreeKb(); //在/proc/pID/reclaim文件中写入节点值 fileOutputStream fos = new fileOutputStream("/proc/" + pID + "/reclaim"); fos.write(action.getBytes()); fos.close(); long[] RSSAfter = Process.getRSS(pID); long end = SystemClock.uptimeMillis(); long time = end - start; long zramFreeKbAfter = DeBUG.getZramFreeKb(); EventLog.writeEvent(EventLogTags.AM_COMPACT, pID, name, action, RSSBefore[0], RSSBefore[1], RSSBefore[2], RSSBefore[3], RSSAfter[0] - RSSBefore[0], RSSAfter[1] - RSSBefore[1], RSSAfter[2] - RSSBefore[2], RSSAfter[3] - RSSBefore[3], time, lastCompactAction, lastCompactTime, lastOomAdj, procState, zramFreeKbBefore, zramFreeKbAfter - zramFreeKbBefore); // Note that as above not taking mPhenoTypeFlagLock here to avoID locking // on every single compaction for a flag that will seldom change and the // impact of reading the wrong value here is low. if (mRandom.nextfloat() < mStatsdSampleRate) { StatsLog.write(StatsLog.APP_COMPACTED, pID, name, pendingAction, RSSBefore[0], RSSBefore[1], RSSBefore[2], RSSBefore[3], RSSAfter[0], RSSAfter[1], RSSAfter[2], RSSAfter[3], time, lastCompactAction, lastCompactTime, lastOomAdj, ActivityManager.processstateAmtoproto(procState), zramFreeKbBefore, zramFreeKbAfter); } synchronized (mAm) { proc.lastCompactTime = end; proc.lastCompactAction = pendingAction; } if (action.equals(COMPACT_ACTION_FulL) || action.equals(COMPACT_ACTION_ANON)) { mLastCompactionStats.put(pID, new LastCompactionStats(RSSAfter)); } } catch (Exception e) { // nothing to do, presumably the process dIEd } finally { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; } case COMPACT_SYstem_MSG: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "compactSystem"); compactSystem();//native方法 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; } } }}
compactSystem是个native方法,对应JNI如下:
frameworks/base/services/core/jni/com_androID_server_am_AppCompactor.cpp// 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; 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()) { 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) { continue; } std::string reclaim_path = StringPrintf("/proc/%s/reclaim", current->d_name); WriteStringTofile(std::string("all"), reclaim_path); //写all }}
FW逻辑总结:
应用进程:
一系列判断和赋值之后,在/proc/pID/reclaim文件中写入action,
这里action主要看两种:
some对应"file"
full、presistent、bfgs都对应"all"
非应用进程(除system_server):
在/proc/pID/reclaim文件中写入”all"
FW最终写了节点,那么接下来看看kernel的逻辑:
kernel/msm-4.19/fs/proc/base.c#ifdef CONfig_PROCESS_RECLaim REG("reclaim", 0200, proc_reclaim_operations),#endif
注册节点执行proc_reclaim_operations
kernel/msm-4.19/fs/proc/task_mmu.cconst struct file_operations proc_reclaim_operations = { .write = reclaim_write, .llseek = noop_llseek,};
在节点write *** 作后,对应会触发reclaim_write
static ssize_t reclaim_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){ struct task_struct *task;//进程 char buffer[200]; struct mm_struct *mm;//内存 struct vm_area_struct *vma;//虚拟内存区域 enum reclaim_type type;//回收类型: RECLaim_file, RECLaim_ANON, RECLaim_ALL, RECLaim_RANGE, char *type_buf; struct mm_walk reclaim_walk = {}; unsigned long start = 0;//起始 unsigned long end = 0;//结尾 struct reclaim_param rp; int ret; memset(buffer, 0, sizeof(buffer));//为新申请内存进行初始化 if (count > sizeof(buffer) - 1) count = sizeof(buffer) - 1; if (copy_from_user(buffer, buf, count)) return -EFAulT; //从FW传进来的内容看:目前只有file 和 all两种,这里就是解析对应的回收类型。 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 if (isdigit(*type_buf)) type = RECLaim_RANGE; else goto out_err; if (type == RECLaim_RANGE) { char *token; unsigned long long len, len_in, tmp; token = strsep(&type_buf, " "); if (!token) goto out_err; tmp = memparse(token, &token); if (tmp & ~PAGE_MASK || tmp > ulONG_MAX) goto out_err; start = tmp; token = strsep(&type_buf, " "); if (!token) goto out_err; len_in = memparse(token, &token); len = (len_in + ~PAGE_MASK) & PAGE_MASK; if (len > ulONG_MAX) goto out_err; /* * Check to see whether len was rounded up from small -ve * to zero. */ if (len_in && !len) goto out_err; end = start + len; if (end < start) goto out_err; } task = get_proc_task(file->f_path.dentry->d_inode);//获取进程 if (!task) return -ESRCH; mm = get_task_mm(task);//获取进程的内存struct if (!mm) goto out; [reclaim_walk.mm](http://reclaim_walk.mm) = mm; reclaim_walk.pmd_entry = reclaim_pte_range;//最终触发回收 rp.nr_to_reclaim = INT_MAX; rp.nr_reclaimed = 0; reclaim_walk.private = &rp; down_read(&mm->mmap_sem);//读信号量 if (type == RECLaim_RANGE) { vma = find_vma(mm, start); while (vma) { if (vma->vm_start > end) break; if (is_vm_hugetlb_page(vma)) continue; rp.vma = vma; ret = walk_page_range(max(vma->vm_start, start), min(vma->vm_end, end), &reclaim_walk); if (ret) break; vma = vma->vm_next; } } else { //遍历当前进程所占用的虚拟地址 for (vma = mm->mmap; vma; vma = vma->vm_next) { if (is_vm_hugetlb_page(vma)) continue; if (type == RECLaim_ANON && vma->vm_file)//anon对应回收匿名页 continue; if (type == RECLaim_file && !vma->vm_file)//file对应回收文件页 continue; rp.vma = vma; //walk_page_range的功能就是遍历页表,并调用回调函数进行处理,回调函数都是定义在mm_walk中。 ret = walk_page_range(vma->vm_start, vma->vm_end, &reclaim_walk); if (ret) break; } } flush_tlb_mm(mm); up_read(&mm->mmap_sem); mmput(mm);out: put_task_struct(task); return count;out_err: return -EINVAL;}
walk_page_range最终会回调到:reclaim_walk.pmd_entry,而它会触发执行reclaim_pte_range
#ifdef CONfig_PROCESS_RECLaimint reclaim_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, struct mm_walk *walk){ struct reclaim_param *rp = walk->private; struct vm_area_struct *vma = rp->vma; pte_t *pte, ptent; spinlock_t *ptl; struct page *page; List_head(page_List); int isolated; int reclaimed; split_huge_pmd(vma, addr, pmd); if (pmd_trans_unstable(pmd) || !rp->nr_to_reclaim) return 0;cont: isolated = 0; pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); for (; addr != end; pte++, addr += PAGE_SIZE) { ptent = *pte; if (!pte_present(ptent)) continue; page = vm_normal_page(vma, addr, ptent); if (!page) continue; if (page_mapcount(page) != 1) continue; if (isolate_lru_page(page)) continue; /* MADV_FREE clears pte dirty bit and then marks the page * lazyfree (clear SwapBacked). Inbetween if this lazyfreed page * is touched by user then it becomes dirty. PPR in * shrink_page_List in try_to_unmap finds the page dirty, marks * it back as PageSwapBacked and skips reclaim. This can cause * isolated count mismatch. */ if (PageAnon(page) && !PageSwapBacked(page)) { putback_lru_page(page); continue; } List_add(&page->lru, &page_List); inc_node_page_state(page, NR_ISolATED_ANON + page_is_file_cache(page)); isolated++; rp->nr_scanned++; if ((isolated >= SWAP_CLUSTER_MAX) || !rp->nr_to_reclaim) break; } pte_unmap_unlock(pte - 1, ptl); //最终调用reclaim_pages_from_List来触发回收 reclaimed = reclaim_pages_from_List(&page_List, vma); rp->nr_reclaimed += reclaimed; rp->nr_to_reclaim -= reclaimed; if (rp->nr_to_reclaim < 0) rp->nr_to_reclaim = 0; if (rp->nr_to_reclaim && (addr != end)) goto cont; cond_resched(); return (rp->nr_to_reclaim == 0) ? -EPIPE : 0;}
最终调用:
#ifdef CONfig_PROCESS_RECLaimunsigned long reclaim_pages_from_List(struct List_head *page_List, struct vm_area_struct *vma){ struct scan_control sc = { .gfp_mask = GFP_KERNEL, .priority = DEF_PRIORITY, .may_writepage = 1, .may_unmap = 1, .may_swap = 1, .target_vma = vma, }; unsigned long nr_reclaimed; struct page *page; List_for_each_entry(page, page_List, lru) ClearPageActive(page); nr_reclaimed = shrink_page_List(page_List, NulL, &sc, TTU_IGnorE_ACCESS, NulL, true); while (!List_empty(page_List)) { page = lru_to_page(page_List); List_del(&page->lru); dec_node_page_state(page, NR_ISolATED_ANON + page_is_file_cache(page)); putback_lru_page(page); } return nr_reclaimed;}#endif
到shrink_page_List,这就很明显了,根据lru触发page页回收,后面就不继续跟了。
进程的内存管理
总结:
app compaction 是以进程为单位触发文件页、匿名页回收的内存优化策略,上层FW提供优化的进程依据。该策略在kernel很早就已经有了,只是到AndroID Q才开始在FW上层做逻辑,AOSP该功能目前是默认关闭的,并且策略稍显简单,可以研究下自定义一些策略。
对应kernel patch:
Michan Kim
https://lore.kernel.org/patchwork/patch/688100/
原文地址:https://www.jianshu.com/p/ae4ca096201a
总结以上是内存溢出为你收集整理的Android Q app内存压缩优化方案介绍全部内容,希望文章能够帮你解决Android Q app内存压缩优化方案介绍所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)