美文网首页
Android的OOM_ADJ

Android的OOM_ADJ

作者: jimjayce | 来源:发表于2018-09-04 20:03 被阅读0次

    OOM_ADJ (Out of Memory Adjustment)是android系统在内存不足情况下进行内存调整的重要参数,我先看一下OOM_ADJ 都有哪些取值:

    OOM_ADJ OOM_SCORE_ADJ 分类 透过何种方式达到
    -17 (native) -1000 Native 进程
    -12 (persistent) -900 框架或常驻应用 在AndroidManifest.xml设persistent = true
    0 (foreground) 0 前台应用 Case 1: 目前正在前台的应用。Case2: 应用正在运行onRecieve()、或是Service.onCreate()、onStartCommand()。
    1 (visible) 100 Case 1:被常驻应用bind住的service或provider。 Case 2:虽非前台,但看得到界面的应用。
    2 (perceptible) 200 Foreground service、IME startService()且呼叫startForeground()
    5 (A service) 500 服务 startService()
    6 (home) 600 桌面在后台时
    7 (previous) 700 前一个按home键离开的应用
    8 (B service) 800 服务 startService()
    9 ~15 (cached) 900~906 Case 1:单纯只有activity的应用。Case2:已执行receiver、service、provider。Case 3:没有stopService()且内含activity

    一般常造成问题的状况
    1.OOM_ADJ < 0

    • 砍不到
    • Foreground scheduling

    2.OOM_ADJ = 1, 2

    • 砍不到
    • Foreground scheduling

    3.OOM_ADJ = 5,或>=8

    • 砍了又马上重启(功耗、效能)
    • 重启时callback以foreground scheduling运行

    共通影响:应用切换的性能(Multitasking performance)

    查询应用的OOM_ADJ

    image.png

    OOM_ADJ与编程

    例如是否有人考虑过这么解决broadcast timeout ANR问题

    //AndroidManifest.xml
    <receiver android:name=".MyReceiver">
        <action android:name=“android.intent.action.BOOT_COMPLETED”/>
    </receiver>
    
    //MyReceiver.java
    public class MyReceiver extends BroadcastReceiver{
        public void onReceive(Context context, Intent intent) {
            new Thread() {
                public void run() {
                //做超过10秒的事
                }
            }.start();
        }
    }
    

    当应用的执行离开了onReceive(),就等同告诉框架,此应用已经执行完receiver 了,不需要再保持高优先级的OOM_ADJ。当系统进行大量广播、或内存较紧时,进程可能很快就被砍掉。此问题在开机或FOTA后特别容易发生。ANR触发原理:https://developer.android.com/training/articles/perf-anr#java

    线程无法影响OOM_ADJ,只有进程的行为能影响

    • Broadcast Receiver
    • 在执行onReceive() 时,应用的OOM_ADJ 为0
    • 离开onReceive() 后,视应用其他的状态决定OOM_ADJ
    • Started Service

    在执行onCreate()、onStartCommand() 时,应用的OOM_ADJ 为0。之后依序经历几种变化:

    • Service A:Service 刚运行时
    • Service B:若系统同时运行的Service 过多,较早运行的Service 会放到Service B
    • Cached:运行超过30分钟、或曾经启动过activity

    Foreground service 能保持OOM_ADJ 处于2,且跟前台应用抢占CPU 时有相同优先级。但滥用foreground service,会导致内存问题

    • Bind Service
    • 若进程A bind 住进程B 的service,ActivityManager会将B的重要性提升至与A 相同。如果A使用完服务后,忘了调用unbindService(),B的重要性就降不下来,极端情况是,若A是常驻的,会导致B也变成常驻了,引发系统性内存问题
    • 若进程A bind住的service位在同一进程,则OOM_ADJ 不会因此有任何改变
    • Provider
    • 若进程A查询进程B的provider得到Cursor,在进程A关闭Cursor之前,ActivityManager会保持provider connection,此时B的OOM_ADJ将提升至与A相同。若进程A属于常驻应用,则B也跟着变成常驻了,就形成严重的内存问题
    • 若应用B的UID为system UID,不可开放content provider给其他应用使用。在安卓的特殊机制中,只要进程A存取(insert/update/delete/pquery) 了B的provider,provider connection 就不再结束

    OOM_ADJ分析案例

    • 案例一:
      笔记本应用,如何在用户按home键时存下草稿?
    public class MyActivityextends Activity {
        public void onPause() {
            //储存草稿
        }
    }
    

    方案一可能导致ANR.

    public class MyActivityextends Activity {
        public void onPause() {
            new Thread() {
                public void run() {
                    //储存草稿
                }
            }.start();
        }
    }
    

    方案二,储存草稿可能做不完…
    综上,在手机内存不足的情况,应用退到后台后,可能很快被砍掉,但若启动service,则因为此进程启动过activity,依然无法提高OOM_ADJ。故,此种情况适合使用foreground service ,但时间不可太长。

    • 案例二:
      发送短信的Service,若跟Activity运行在同一个进程,有何潜藏危机?
      当用户送出短信时,若马上按下back 键离开应用,此时虽然SendSmsService还在运行,但由于这个进程执行过Activity,故OOM_ADJ会掉到>= 9。若手机内存不足,可能进程会很快被砍掉,导致功能出错

    常驻应用

    • 狭义

    在AndroidManifest.xml 设定persistent =true 的应用

    • 广义

    应用透过某种方式,直接、或间接持续运行在内存都算,例如:

    • 不停止的started service
    • 常驻应用在后台透过bind service 绑住另一进程、且不释放
    • 常驻应用在后台透过provider 绑住另一进程、且不释放
    • 应用透过AlarmManager或类似方式,不断启动,导致被砍了也频繁重启运行在后台

    相关文章

      网友评论

          本文标题:Android的OOM_ADJ

          本文链接:https://www.haomeiwen.com/subject/aeenwftx.html