Android性能优化OOM内存管理-ADJ

前言

OOM_ADJ (Out of Memory Adjustment)是android系统在内存不足情况下进行内存调整的重要参数。在处理app启动速度的时候,可以设置主线程的优先级,保证主线程占用的cpu足够久。进程的oom_adj,决定了当内存不够的时候,lmk会根据oom_adj的大小依次释放内存。在前面介绍Activity页面启动路程过程中见到了更新adj的相关方法,但是没有深入介绍,这里分析一些相关实现。

更新adj

final boolean realStartActivityLocked(ActivityRecord r,
        ProcessRecord app, boolean andResume, boolean checkConfig)
        throws RemoteException {

    r.startFreezingScreenLocked(app, 0);
     //更新Lur
    mService.updateLruProcessLocked(app, true, null);
    //更新ADJ
    mService.updateOomAdjLocked();
     xxxx
     
    //通过Binder 远程调用Activity的onCreate onResume等生命周期
    app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
        System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
        r.compat, r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState,
        results, newIntents, !andResume, mService.isNextTransitionForward(),
        profilerInfo);
     
    return true;
}

在启动页面的流程中存在一个名为realStartActivityLocked的方法,这个方法会通过Binder 远程调用Activity的onCreate,onResume 等生命周期方法,在回调生命周期之前调用了updateLruProcessLocked以及updateOomAdjLocked 这两个方法。这两个方法都与进程的优先级有关系。

updateLruProcessLocked

 final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
                                      ProcessRecord client) {
        //hasActivity用来表示某个app中是否包含activity组件
        //1.app本身确实包含activity组件;
        //2. app本身有service,并且有另外一个含有activity的app链接到此app的service上;
        //3. 该app启动serivce的时候带有标记BIND_TREAT_LIKE_ACTIVITY。
        final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities
                || app.treatLikeActivity;

        //目前,并没有考虑进程中是否含有Service
        //因此,虽然理论上定义了Service相关的进程分类,但并没有实现对应的管理策略
        //在以下代码中,hasService一直为false
        final boolean hasService = false; // not impl yet. app.services.size() > 0;
        if (!activityChange && hasActivity) {
            // The process has activities, so we are only allowing activity-based adjustments
            // to move it.  It should be kept in the front of the list with other
            // processes that have activities, and we don't want those to change their
            // order except due to activity operations.
            return;
        }
        //计数器,记录该函数被调用了多少次,也就是LRU被更新了多少次。
        mLruSeq++;
        final long now = SystemClock.uptimeMillis();
        app.lastActivityTime = now;
     
        // First a quick reject: if the app is already at the position we will
        // put it, then there is nothing to do.
        if (hasActivity) {
            final int N = mLruProcesses.size();
            //如果要插入的app已经在mLruProcesses顶端了,就不用插入了
            if (N > 0 && mLruProcesses.get(N-1) == app) {
                if (DEBUG_LRU) Slog.d(TAG, "Not moving, already top activity: " + app);
                return;
            }
        } else {
            //将其插入到Service 的开头
            if (mLruProcessServiceStart > 0
                    && mLruProcesses.get(mLruProcessServiceStart-1) == app) {
                if (DEBUG_LRU) Slog.d(TAG, "Not moving, already top other: " + app);
                return;
            }
        }
     
        int lrui = mLruProcesses.lastIndexOf(app);
     
        // persistent app,这部分app不会被杀死,永远在运行,
        //
        if (app.persistent && lrui >= 0) {
            //如果persistent app 已经在列表里面了那么不作处理。
            // We don't care about the position of persistent processes, as long as
            // they are in the list.
            if (DEBUG_LRU) Slog.d(TAG, "Not moving, persistent: " + app);
            return;
        }
     
        //lrui>=0,说明LRU中之前记录过当前进程的信息
        //即该进程不是新创建的
        //那么在调整之前,需要先将之前的记录删除
        if (lrui >= 0) {
            if (lrui < mLruProcessActivityStart) {
                //此进程没有包含Activity
                mLruProcessActivityStart--;
            }
            if (lrui < mLruProcessServiceStart) {
                //此进程没有服务,是个其他类型的进程
                mLruProcessServiceStart--;
            }
            //移除进程,后面会再次添加
            mLruProcesses.remove(lrui);
        }
        //nextIndex主要用于记录
        //当前进程绑定的Service或ContentProvider对应的进程,
        //应该插入的位置 (对应进程中仅含有Service和Provider时才需要处理)
        //后文将看到该值的使用情况
        int nextIndex;
        if (hasActivity) {
            final int N = mLruProcesses.size();
     
            if (app.activities.size() == 0 && mLruProcessActivityStart < (N-1)) {
                //该App没有Activity, 但是有一个有Activity的app启动了该App的一个Service。
                //mLruProcessActivityStart < (N-1) 表示App 不是当前在显示的页面。
                mLruProcesses.add(N-1, app);
              //举一个具体的例子,当前显示的App A 打开属于另一个App B的Service,此时当前显示的App A就在
              //N 这个位置,被打开的Service 所在的App B在N-1 这个位置。
     
                // To keep it from spamming the LRU list (by making a bunch of clients),
                // we will push down any other entries owned by the app.
                // 下面的代码,是为了调整不同用户之间的公平性;
                // 当前用户新启动了一个进程,将该用户对应的其它进程,适当往前挪动一下 (优先被kill)
                final int uid = app.info.uid;
                // 为了防止某个app中的service绑定了一群client从而导致LRU中顶部大部分都是这些client
                //,这里需要将这些client往下移动,以防止某些app通过和某个app的service绑定从而提升自己在LRU中位置。
                for (int i=N-2; i>mLruProcessActivityStart; i--) {
                    ProcessRecord subProc = mLruProcesses.get(i);
                    //遍历找到第一个与app 的uid
                    if (subProc.info.uid == uid) {
                        if (mLruProcesses.get(i-1).info.uid != uid) {
                            //交换i与i-1位置的进程,
                            ProcessRecord tmp = mLruProcesses.get(i);
                            mLruProcesses.set(i, mLruProcesses.get(i-1));
                            mLruProcesses.set(i-1, tmp);
                            i--;
                        }
                        //还是以上面那个例子为例。 A 在打开B之后有打开另一个App C 的Service。
                        //此时 A,B,C 的位置是 N,N-1,N-2 ,由于B C的uid 一样,
                        //此时B也就是先打开的服务可能会一直向后移动直到mLruProcessActivityStart这个位置,
                    } else {
                        // A gap, we can stop here.
                        break;
                    }
                }
            } else {
                // Process has activities, put it at the very tipsy-top.
                if (DEBUG_LRU) Slog.d(TAG, "Adding to top of LRU activity list: " + app);
                //进程具有activity,在N位置添加,也就是在栈顶添加,此时app 一般就是要显示的app。
                mLruProcesses.add(app);
            }
            nextIndex = mLruProcessServiceStart;
        } else if (hasService) {
            // Process has services, put it at the top of the service list.
            //不走这个分支,hasService 总是false,
            if (DEBUG_LRU) Slog.d(TAG, "Adding to top of LRU service list: " + app);
            mLruProcesses.add(mLruProcessActivityStart, app);
            nextIndex = mLruProcessServiceStart;
            mLruProcessActivityStart++;
        } else  {
            // Process not otherwise of interest, it goes to the top of the non-service area.
            // 一般走这里,
            int index = mLruProcessServiceStart;
            //一般情况下client == null, 这个分支不走
            if (client != null) {
                //client 表示一个另一个进程,此进程可能具有页面,也可没有,但是这个进程打开了
                //一个只有服务得进程,那么只有服务的进程需要排在client进程的下面
                // If there is a client, don't allow the process to be moved up higher
                // in the list than that client.
                int clientIndex = mLruProcesses.lastIndexOf(client);
                if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG, "Unknown client " + client
                        + " when updating " + app);
                if (clientIndex <= lrui) {
                    // Don't allow the client index restriction to push it down farther in the
                    // list than it already is.
                    clientIndex = lrui;
                }
                if (clientIndex >= 0 && index > clientIndex) {
                    //此时表示client 也是一个只有服务的进程而且client在app进程的下面,此时需要
                    //调整添加app进程的位置,调整之后app的位置是clientIndex,client的位置是clientIndex+1
                    index = clientIndex;
                }
            }
            if (DEBUG_LRU) Slog.d(TAG, "Adding at " + index + " of LRU list: " + app);
            //添加进程
            mLruProcesses.add(index, app);
            nextIndex = index-1;
            mLruProcessActivityStart++;
            mLruProcessServiceStart++;
        }
     
        // If the app is currently using a content provider or service,
        // bump those processes as well.
        //本进程打开了service或者是ContentProvider,如果这个Service或者ContentProvider
        // 是定义自己App 里面那么此处没啥影响。如果是定义在另一个App里面则有影响。
        //这里的微调分为两种情况:
        //第一是service所在的进程的位置调整到本进程之后,
        //第二是将ContentProvider所在的进程位置调整到本进程之后。
        //调整的方式都是使用updateLruProcessInternalLocked方法,
        for (int j=app.connections.size()-1; j>=0; j--) {
            ConnectionRecord cr = app.connections.valueAt(j);
            if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
                    && cr.binding.service.app != null
                    && cr.binding.service.app.lruSeq != mLruSeq
                    && !cr.binding.service.app.persistent) {
                nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
                        "service connection", cr, app);
            }
        }
        for (int j=app.conProviders.size()-1; j>=0; j--) {
            ContentProviderRecord cpr = app.conProviders.get(j).provider;
            if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) {
                nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
                        "provider reference", cpr, app);
            }
        }
    }    private final int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
                                                     String what, Object obj, ProcessRecord srcApp) {
     
        //srcApp 打开 app 的一个Service 或者ContentProvider
     
        app.lastActivityTime = now;
        //如果有Activity,不做调整
        if (app.activities.size() > 0) {
            // Don't want to touch dependent processes that are hosting activities.
            return index;
        }
        //如果进程不在mLruProcess中,就返回
        int lrui = mLruProcesses.lastIndexOf(app);
        if (lrui < 0) {
            Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
                    + what + " " + obj + " from " + srcApp);
            return index;
        }
        //如果进程的位置高于需要调整的位置,不做调整
        if (lrui >= index) {
            // Don't want to cause this to move dependent processes *back* in the
            // list as if they were less frequently used.
            return index;
        }
        //如果目前进程的位置比mLruProcessActivityStart还要高,不调整
        if (lrui >= mLruProcessActivityStart) {
            // Don't want to touch dependent processes that are hosting activities.
            return index;
        }
        //走到这里表示lrui 0) {
            index--;
        }
        if (DEBUG_LRU) Slog.d(TAG, "Moving dep from " + lrui + " to " + index
                + " in LRU list: " + app);
        //
        mLruProcesses.add(index, app);
        return index;
    }
     
    // 例如 当前显示的App  A打开了一个App B 的一个Service ,由于App  A 是当前显示的App,优先级最高,
    //此时A 使用的Service 所在的App B 也应该尽可能的提高等级避免内存回收,此时会将App B 放到mLruProcessServiceStart 这个位置。
    //假如非得回收内存的话会先回收0-mLruProcessServiceStart 之间的进程占据的内存。

mLruProcesses 是一个列表,其本分为三个部分 0--mLruProcessServiceStart 用于保存其他进程;

mLruProcessServiceStart -- mLruProcessActivityStart 用于保存服务进程,但是实际情况下这个区域的大小是0,也即是服务进程实际也是放在了其他进程区域。

mLruProcessActivityStart--end 保存的有Activity的进程。 每次添加Activity 进程都是在end位置,在mLruProcessServiceStart位置添加服务进程或者其他进程。

位置越大的进程优先级越高越不容易被回收。

每次调用updateLruProcessLocked调整某个进程的位置的时候也会调整与之相关的进程的位置,例如调整进程A 的位置 就要顺便调整A 启动的Service 以及ContentProvider 所在的进程位置。

————————————————

版权声明:本文为CSDN博主「昨夜西风在吹」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_31469589/article/details/11796935

以上就是有关Android内存管理ADJ讲解;有关更多Android 开发技术性能调优学习;大家私信:“手册”《Android性能优化手册》获取相关学习资料。

结尾(心灵的鸡汤)

我相信,梦想只要能坚持,就一定能成为现实。就像代表着永恒的天蓝色。就让这小小的梦想的种子,在我们心中,渐渐发芽、成长,在心中开出美丽、绚烂的花。让我们努力飞翔,乘着梦想的翅膀,飞到成功的远方。

展开阅读全文

页面更新:2024-05-04

标签:优先级   主线   生命周期   进程   定义   内存   性能   位置   梦想   链接   方法

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top