Android Q app内存压缩优化方案介绍

Android Q app内存压缩优化方案介绍,第1张

概述AndroidQapp内存压缩优化方案介绍原创文章,谢绝转载!AndroidQ新增了部分系统性能优化方案,这里简单学习下,本篇文章先分析appcompaction。一、愿景:在保证后台进程尽量不被杀的基础上减少它们的内存占用。二、思路:AMS与Kernel层联动对满足一定条件的App进行内存压缩。goog AndroID Q app内存压缩优化方案介绍

原创文章,谢绝转载!

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内存压缩优化方案介绍所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1025790.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存