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.pngOOM_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或类似方式,不断启动,导致被砍了也频繁重启运行在后台
网友评论