Android之lmkd使用篇

一、简介

进程的启动分冷启动和热启动,当用户退出某一个进程的时候,并不会真正的将进程退出,而是将这个进程放到后台,以便下次启动的时候可以马上启动起来,这个过程名为热启动,这也是Android的设计理念之一。这个机制会带来一个问题,每个进程都有自己独立的内存地址空间,随着应用打开数量的增多,系统已使用的内存越来越大,就很有可能导致系统内存不足。

lmkd(Low Memory Killer Daemon)是低内存终止守护进程,用来监控运行中android系统内存的状态,通过终止最不必要的进程来应对内存压力较高的问题,使系统以可接受的水平运行。

这里以Android7.1和Android10为对象分析

二、实现原理

所有应用进程都是从zygote孵化出来的,记录在AMS中mLruProcesses列表中,由AMS进行统一管理,AMS中会根据进程的状态更新进程对应的oom_adj值,这个值会通过socket传递给lmkd。lmdk根据内核的版本情况,或传递给kernel或自身处理低内存回收机制。为腾出更多的内存空间,在内存达到一定阀值时会触发清理oom_adj值高的进程。

三、必备知识
1.内存基础概念

在linux下表示内存的耗用情况有四种不同的表现形式:
 VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
 RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)
 PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
 USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)

VSS:VSS表示一个进程可访问的全部内存地址空间的大小。这个大小包括了进程已经申请但尚未使用的内存空间。在实际中很少用这种方式来表示进程占用内存的情况,用它来表示单个进程的内存使用情况是不准确的。

RSS:表示一个进程在RAM中实际使用的空间地址大小,包括了全部共享库占用的内存,这种表示进程占用内存的情况也是不准确的。

PSS:表示一个进程在RAM中实际使用的空间地址大小,它按比例包含了共享库占用的内存。假如有3个进程使用同一个共享库,那么每个进程的PSS就包括了1/3大小的共享库内存。这种方式表示进程的内存使用情况较准确,但当只有一个进程使用共享库时,其情况和RSS一模一样。

USS:表示一个进程本身占用的内存空间大小,不包含其它任何成分,这是表示进程内存大小的最好方式!

总结
VSS>=RSS>=PSS>=USS

2.ADJ值可在ProcessList中查询
注:adj越大,越容易被kill,对于同等的adj值,内存占有越大的越容易被kill.

    static final int UNKNOWN_ADJ = 1001;

    // This is a process only hosting activities that are not visible,
    // so it can be killed without any disruption.
    static final int CACHED_APP_MAX_ADJ = 906;
    static final int CACHED_APP_MIN_ADJ = 900;

    // The B list of SERVICE_ADJ -- these are the old and decrepit
    // services that aren't as shiny and interesting as the ones in the A list.
    static final int SERVICE_B_ADJ = 800;

    // This is the process of the previous application that the user was in.
    // This process is kept above other things, because it is very common to
    // switch back to the previous app.  This is important both for recent
    // task switch (toggling between the two top recent apps) as well as normal
    // UI flow such as clicking on a URI in the e-mail app to view in the browser,
    // and then pressing back to return to e-mail.
    static final int PREVIOUS_APP_ADJ = 700;

    // This is a process holding the home application -- we want to try
    // avoiding killing it, even if it would normally be in the background,
    // because the user interacts with it so much.
    static final int HOME_APP_ADJ = 600;

    // This is a process holding an application service -- killing it will not
    // have much of an impact as far as the user is concerned.
    static final int SERVICE_ADJ = 500;

    // This is a process with a heavy-weight application.  It is in the
    // background, but we want to try to avoid killing it.  Value set in
    // system/rootdir/init.rc on startup.
    static final int HEAVY_WEIGHT_APP_ADJ = 400;

    // This is a process currently hosting a backup operation.  Killing it
    // is not entirely fatal but is generally a bad idea.
    static final int BACKUP_APP_ADJ = 300;

    // This is a process only hosting components that are perceptible to the
    // user, and we really want to avoid killing them, but they are not
    // immediately visible. An example is background music playback.
    static final int PERCEPTIBLE_APP_ADJ = 200;

    // This is a process only hosting activities that are visible to the
    // user, so we'd prefer they don't disappear.
    static final int VISIBLE_APP_ADJ = 100;
    static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;

    // This is the process running the current foreground app.  We'd really
    // rather not kill it!
    static final int FOREGROUND_APP_ADJ = 0;

    // This is a process that the system or a persistent process has bound to,
    // and indicated it is important.
    static final int PERSISTENT_SERVICE_ADJ = -700;

    // This is a system persistent process, such as telephony.  Definitely
    // don't want to kill it, but doing so is not completely fatal.
    static final int PERSISTENT_PROC_ADJ = -800;

    // The system process runs at the default adjustment.
    static final int SYSTEM_ADJ = -900;

    // Special code for native processes that are not being managed by the system (so
    // don't have an oom adj assigned by the system).
    static final int NATIVE_ADJ = -1000;

四、查询命令
1.adb shell dumpsys activity o
可查询app对应的adj等级,同时可通过OOM levels查看在某一个等级升级内存清理

注:
(ActivityManagerService dump 可查 activity o)

C:\Users\99418>adb shell dumpsys activity o
[dump_debug] dumpAppId:-1, dumpPackage:null
  OOM levels:
    -900: SYSTEM_ADJ (   73,728K)
    -800: PERSISTENT_PROC_ADJ (   73,728K)
    -700: PERSISTENT_SERVICE_ADJ (   73,728K)
      0: FOREGROUND_APP_ADJ (   73,728K)
     100: VISIBLE_APP_ADJ (   92,160K)
     200: PERCEPTIBLE_APP_ADJ (  110,592K)
     250: PERCEPTIBLE_LOW_APP_ADJ (  129,024K)
     300: BACKUP_APP_ADJ (  221,184K)
     400: HEAVY_WEIGHT_APP_ADJ (  221,184K)
     500: SERVICE_ADJ (  221,184K)
     600: HOME_APP_ADJ (  221,184K)
     700: PREVIOUS_APP_ADJ (  221,184K)
     800: SERVICE_B_ADJ (  221,184K)
     900: CACHED_APP_MIN_ADJ (  221,184K)
     999: CACHED_APP_MAX_ADJ (  322,560K)

  Process OOM control (43 total, non-act at 1, non-svc at 1):
    PERS #42: sys    F/ /PER  trm: 0 2811:system/1000 (fixed)
        oom: max=-900 curRaw=-900 setRaw=-900 cur=-900 set=-900 //-900 adj最高级别SYSTEM_ADJ
        state: cur=PER  set=PER  lastPss=144MB lastSwapPss=21MB lastCachedPss=0.00
        cached=false empty=false hasAboveClient=false
    ···
    Proc # 0: fore   R/A/BFGS trm: 0 3930:com.android.launcher3/u0a98 (service)
        com.android.launcher3/com.android.quickstep.TouchInteractionService<=Proc{2979:com.android.systemui/u0a97}
        oom: max=1001 curRaw=0 setRaw=0 cur=0 set=0 //0 ADJ级别FOREGROUND_APP_ADJ
        state: cur=BFGS set=BFGS lastPss=39MB lastSwapPss=20MB lastCachedPss=0.00
        cached=false empty=false hasAboveClient=false
    ···
    Proc #31: cch+75 B/ /CEM  trm: 0 4303:com.android.externalstorage/u0a49 (cch-empty)
        oom: max=1001 curRaw=975 setRaw=975 cur=975 set=975  //975 ADJ值太大,容易被清理
        state: cur=CEM  set=CEM  lastPss=6.7MB lastSwapPss=0.00 lastCachedPss=6.7MB
        cached=true empty=true hasAboveClient=false
  mHomeProcess: ProcessRecord{6b95785 3930:com.android.launcher3/u0a98}
  mPreviousProcess: null

2.adb shell dumpsys activity p
针对进程的oom查询更加精确

(ActivityManagerService dump 可查 activity p)
C:\Users\99418>adb shell dumpsys activity p
  ···
  *APP* UID 10098 ProcessRecord{6b95785 3930:com.android.launcher3/u0a98}  //uid及pid
    user #0 uid=10098 gids={50098, 20098, 9997}
    mRequiredAbi=arm64-v8a instructionSet=null
    dir=/product/priv-app/MtkLauncher3QuickStep/MtkLauncher3QuickStep.apk //安装位置 publicDir=/product/priv-app/MtkLauncher3QuickStep/MtkLauncher3QuickStep.apk data=/data/user/0/com.android.launcher3  //app数据位置
    packageList={com.android.launcher3}  //包名
    compat={240dpi always-compat}
    thread=android.app.IApplicationThread$Stub$Proxy@9e5af
    pid=3930 starting=false
    lastActivityTime=-1h2m37s269ms lastPssTime=-1m3s479ms pssStatType=0 nextPssTime=+13m56s441ms
    adjSeq=834 lruSeq=234 lastPss=38MB lastSwapPss=13MB lastCachedPss=0.00 lastCachedSwapPss=0.00 //pss 内存情况
    procStateMemTracker: best=1 (1=1 1.5x, 2=1 2.25x)
    cached=false empty=false
    oom: max=1001 curRaw=0 setRaw=0 cur=0 set=0  //oom相关情况
    lastCompactTime=3986304 lastCompactAction=4    mCurSchedGroup=2 setSchedGroup=2 systemNoUi=false trimMemoryLevel=0
    curProcState=6 mRepProcState=6 pssProcState=6 setProcState=6 lastStateTime=-1h1m37s411ms
    hasShownUi=true pendingUiClean=true hasAboveClient=false treatLikeActivity=false
    reportedInteraction=true time=-1h6m45s79ms
    hasClientActivities=false foregroundActivities=true (rep=true)
    lastTopTime=-1h1m37s411ms
    startSeq=17
    mountMode=DEFAULT
    lastRequestedGc=-1h5m21s110ms lastLowMemory=-1h5m21s110ms reportLowMemory=false
    whitelistManager=true
    Activities:
      - ActivityRecord{550ca96 u0 com.android.launcher3/.Launcher t4}
    Recent Tasks:
      - TaskRecord{c8c4dbc #4 I=com.android.launcher3/.Launcher U=0 StackId=0 sz=1}
      ···
    Services:
      - ServiceRecord{a7b3c5e u0 com.android.launcher3/.notification.NotificationListener}
      ···
    Published Providers:
      - com.android.launcher3.LauncherProvider
        -> ContentProviderRecord{2d49145 u0 com.android.launcher3/.LauncherProvider}
      ···
    Connected Providers:
      - 15f9e1/com.android.providers.settings/.SettingsProvider->3930:com.android.launcher3/u0a98 s1/1 u0/0 +1h6m44s879ms
    Receivers:
      - ReceiverList{6197c9 3930 com.android.launcher3/10098/u0 remote:78f0bd0}
      ···
  ···

3.adb shell dumpsys meminfo
系统内存总体情况查询及各个进程的占用情况。例如RAM总共多大、launcher3使用内存情况等等

(ActivityManagerService中MemBinder dump 可查 meminfo
ActivityManagerService.setSystemProcess:
ServiceManager.addService("meminfo", new MemBinder(this));
)
C:\Users\99418>adb shell dumpsys meminfo

Applications Memory Usage (in Kilobytes):
Uptime: 4300431 Realtime: 4384400

Total PSS by process:
    159,633K: system (pid 2811)
     96,309K: com.android.systemui (pid 2979)
     ···
        672K: lmkd (pid 467)
        669K: thermal (pid 677)
        575K: usb_mode_switch (pid 325)

Total PSS by OOM adjustment:
    644,944K: Native
         79,077K: surfaceflinger (pid 468)
         60,618K: zygote (pid 2683)
         35,930K: app_process32 (pid 17377)
         33,885K: zygote64 (pid 2682)
         21,714K: init (pid 1)
         ···
    159,633K: System
        159,633K: system (pid 2811)
    145,094K: Persistent
         96,309K: com.android.systemui (pid 2979)
         ···
     21,633K: Persistent Service
         21,633K: com.android.bluetooth (pid 3709)
     41,161K: Foreground
         41,161K: com.android.launcher3 (pid 3930 / activities)
     17,132K: Visible
          8,801K: android.ext.services (pid 3657)
          8,331K: com.android.smspush (pid 4100)
     18,848K: Perceptible
         18,848K: com.android.inputmethod.latin (pid 3583)
    122,876K: Cached
         28,258K: com.mediatek.security (pid 4325)
         ···

Total PSS by category:
    332,730K: .so mmap
    160,164K: EGL mtrack
     81,429K: Native
     67,972K: .apk mmap
     56,812K: .jar mmap
     ···

Total RAM: 3,921,160K (status normal)
 Free RAM: 2,671,288K (  122,876K cached pss +   695,064K cached kernel + 1,853,348K free)
 Used RAM: 1,234,709K (1,048,445K used pss +   186,264K kernel)
 Lost RAM:   163,273K
     ZRAM:    44,100K physical used for   217,916K in swap (2,156,632K total swap)
   Tuning: 384 (large 512), oom   322,560K, restore limit   107,520K (high-end-gfx)

4.adb shell dumpsys procstats -a
procstats工具用于分析应用内存在一段时间内的使用情况(而不像 meminfo 一样在特定时间点捕获快照)。

(ProcessStatsService dump 可查 procstats
ActivityManagerService.setSystemProcess:
ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
)

C:\Users\99418>adb shell dumpsys procstats -a
···
  * com.android.launcher3 / u0a17 / v25:
      Process com.android.launcher3 (unique, 5 entries):
        SOn /Norm/Top    : +1h16m18s862ms
                  Home   : +10m38s557ms
             Mod /Home   : +4m9s959ms
             Low /Top    : +10m0s237ms
                  Home   : +20h51m49s712ms
                  TOTAL  : +22h32m57s327ms
        myID=1d6fe68 mCommonProcess=1d6fe68 mPackage=com.android.launcher3
···
Memory usage:
  Kernel : 140MB (16 samples)
  Native : 573KB (16 samples)
  Persist: 52MB (12 samples)
  Top    : 5.6MB (33 samples)
  ImpFg  : 56 (9 samples)
  Service: 5.3MB (23 samples)
  Receivr: 418 (17 samples)
  Home   : 4.6MB (3 samples)
  CchEmty: 16MB (13 samples)
  Cached : 204MB (16 samples)
  Free   : 1.3GB (16 samples)
  Z-Ram  : 4.0KB (16 samples)
  TOTAL  : 1.7GB

          Start time: 2013-01-18 16:50:09
  Total elapsed time: +2h20m46s35ms (partial) (swapped-out-pss) libart.so
···

五、配置

lmkd目的为了保护设备在低内存下的正常运行。

1.查询

Android 9及以下
adj阀门值:
/sys/module/lowmemorykiller/parameters/adj

minfree阀门值:
/sys/module/lowmemorykiller/parameters/minfree

例如
Q6-Q260S(S0):/ $ cat /sys/module/lowmemorykiller/parameters/minfree
18432,23040,27648,32256,36864,46080
Q6-Q260S(S0):/ $ cat /sys/module/lowmemorykiller/parameters/adj
0,100,200,300,900,906


Android 10
adb shell getprop sys.lmk.minfree_levels

例如:
C:\Users\99418>adb shell getprop sys.lmk.minfree_levels
18432:0,23040:100,27648:200,32256:250,55296:900,80640:950

xxx:xx //minfree:adj  注意:minfree * 4 = 真实值kb

2.原理

a.框架
system_server <--> lmkd (<--> kernel)

system_server: ActivityManagerService (ProcessList)
lmkd:system/core/lmkd

b.adj和minfree的值都有AMS通过socket传递到lmkd  -- 注:Android 7.1源码
关注核心类、方法、变量:
com.android.server.am.ProcessList

    // Memory pages are 4K.
    static final int PAGE_SIZE = 4*1024;

    // These are the various interesting memory levels that we will give to
    // the OOM killer.  Note that the OOM killer only supports 6 slots, so we
    // can't give it a different value for every possible kind of process.
    private int[] mOomAdj = new int[] {
            FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
            BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ
    };
    // These are the low-end OOM level limits.  This is appropriate for an
    // HVGA or smaller phone with less than 512MB.  Values are in KB.
    private final int[] mOomMinFreeLow = new int[] {
            12288, 18432, 24576,
            36864, 43008, 49152
    };
    // These are the high-end OOM level limits.  This is appropriate for a
    // 1280x800 or larger screen with around 1GB RAM.  Values are in KB.
    private final int[] mOomMinFreeHigh = new int[] {
            73728, 92160, 110592,
            129024, 147456, 184320
    };
    
    //方法参数:displayWidth\displayHeight 关注设备屏幕尺寸。注:adb shell wm size 可查
    private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
        // Scale buckets from avail memory: at 300MB we use the lowest values to
        // 700MB or more for the top values.
        float scaleMem = ((float)(mTotalMemMb-350))/(700-350);//mTotalMemMb 关注设备RAM总量

        // Scale buckets from screen size.
        int minSize = 480*800;  //  384000
        int maxSize = 1280*800; // 1024000  230400 870400  .264
        float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);
        if (false) {
            Slog.i("XXXXXX", "scaleMem=" + scaleMem);
            Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth
                    + " dh=" + displayHeight);
        }

        float scale = scaleMem > scaleDisp ? scaleMem : scaleDisp;
        if (scale < 0) scale = 0;
        else if (scale > 1) scale = 1;
        int minfree_adj = Resources.getSystem().getInteger(
                com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAdjust);
        int minfree_abs = Resources.getSystem().getInteger(
                com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAbsolute);
        if (false) {
            Slog.i("XXXXXX", "minfree_adj=" + minfree_adj + " minfree_abs=" + minfree_abs);
        }

        final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;

        //第一次:更新mOomMinFree数据,由设备RAM和屏幕尺寸决定
        for (int i=0; i= 0) {
            for (int i=0; i= 0) {
            reserve = reserve_abs;
        }

        if (reserve_adj != 0) {
            reserve += reserve_adj;
            if (reserve < 0) {
                reserve = 0;
            }
        }

        if (write) {
            ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));
            buf.putInt(LMK_TARGET);
            for (int i=0; i

3.改动建议

因系统根据RAM和设备屏幕大小经过一轮初始化,一般来说,minfree也不用特定更改。
但是对于1G或2G的RAM,涉及内存使用情况,也可能需要调节。这里就直接overlay
config_lowMemoryKillerMinFreeKbytesAbsolute/config_lowMemoryKillerMinFreeKbytesAdjust即可

六、日志分析

关注TAG(lowmemorykiller) 查看是否有对应的kill行为
Android 9及之前 kill行为由内核层操作,同步在kernel日志中关注kswapd
Android 10 kill行为直接由lmkd操作,主要关注logcat日志即可

七、参考学习

https://www.jianshu.com/p/221f4a246b45
https://www.jianshu.com/p/980b6ce4e051
https://www.jianshu.com/p/7bd3d0ee8a56

http://gityuan.com/2018/05/19/android-process-adj/
https://blog.csdn.net/zhzhangnews/article/details/109671845
https://lightingsui.github.io/2021/01/21/Android%E4%B8%AD%E7%9A%84LowMemoryKiller%E6%9C%BA%E5%88%B6/

https://source.android.com/devices/tech/perf/low-ram
https://source.android.google.cn/devices/architecture/kernel/reqs-interfaces?hl=zh-cn
https://source.android.google.cn/devices/tech/perf/lmkd?hl=zh-cn
https://github.com/reklaw-tech/reklaw_blog/blob/master/Android/ULMK

https://source.android.com/devices/tech/debug/procstats?hl=zh-cn
https://blog.csdn.net/Invoker123/article/details/108632459
https://blog.csdn.net/liaochaoyun/article/details/103035222
https://blog.csdn.net/mcsbary/article/details/104609109

你可能感兴趣的:(Android之lmkd使用篇)