作者:敏华,腾讯前台开发工程师
商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。
| 导语 公司应用,现在说的闪退率一般根据bugly或者TQM上报的crash数据来计算,TDR标准中也会要求游戏的闪退率低于2%。但是,真正的闪退数据可能远不止当前统计的这个数据,因为“低内存被杀”导致的闪退现在还无法监测。“低内存被杀”并不是bug,但是对用户而言就是闪退,与bug引起的闪退并没有任何区别。从WeTest测试统计结果来看,低内存被杀占比约30%。
Android系统中,应用在退出时,其实并没有完全从内存中完全清除。你退出应用之后,还能在任务窗口里面看到你的应用,点击能够重新启动这个应用。启动运行一个程序需要较大的开销,Android这么做的主要目的就是为了再次启动的时候减少开销。
Android 系统将尽量长时间地保持应用进程,但为了新建进程或运行更重要的进程,最终需要清除旧进程来回收内存。 为了确定保留或终止哪些进程,系统会根据进程中正在运行的组件以及这些组件的状态,将每个进程放入“重要性层次结构”中。 必要时,系统中的LowMemoryKiller会首先消除重要性最低的进程,然后是重要性略逊的进程,依此类推,以回收系统资源。
LowMemoryKiller主要通过进程的oom_adj(/proc/(pid)/oom_adj)和oom_score_adj(/proc/(pid)/oom_score_adj)来确定回收进程。oom_adj与oom_score_adj其实是相关的,所以准确来说只需要关注oom_adj即可。LowMemoryKiller会查看当前系统空闲内容
///lowmemorykiller.c
static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc){
.....
step 1:获取当前空闲内存
//基本对应/proc/meminfo 中的 free size
int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;
//基本对应/proc/meminfo 中的 cache size
int other_file = global_page_state(NR_FILE_PAGES) -
global_page_state(NR_SHMEM) -
total_swapcache_pages();
....
//step 2:找出阀值以上最大的最小min_score_adj
for (i = 0; i < array_size; i++) {
minfree = lowmem_minfree[i];
if (other_free < minfree && other_file < minfree) {
min_score_adj = lowmem_adj[i];
break;
}
}
//step 3: oom_score_adj最小,且内存占用最大的进程优先清除
for_each_process(tsk) {
oom_score_adj = p->signal->oom_score_adj;
if (oom_score_adj < min_score_adj) {
task_unlock(p);
continue;
}
tasksize = get_mm_rss(p->mm);
task_unlock(p);
if (tasksize <= 0)
continue;
if (selected) {
if (oom_score_adj < selected_oom_score_adj)
continue;
if (oom_score_adj == selected_oom_score_adj &&
tasksize <= selected_tasksize)
continue;
}
selected = p;
}
step 1:获取当前系统空闲内存,adb shell cat /proc/meminfo可查看
step 2:最小的阀值oom_adj,adb shell cat /sys/module/lowmemorykiller/parameters/adj查看oom_adj划分,adb shell /sys/module/lowmemorykiller/parameters/minfree查看oom_adj对应的最小内存阀值。如果当前内存只有50M了,那么oom_adj>=1的进程就有可能会被清除。
step 3:选择优先级最低oom_adj最小,且内存占用最大的进程优先清除。adb shell /proc/(pid)/oom_adj可查看当前进程adj。
腾讯的游戏在运行过程中,可能会经历哪些adj呢?
1、正常运行,前台应用,adj=0
2、登录时(按home键、来电话时),前一个应用,adj=7
AMS管理进程,在初始化的时候会创建一个应用进程讣告接收对象。当应用进程退出时,讣告对象AppDeathRecipient的binderDied方法会被调用。AMS会查看本次进程的退出时因为LowMemoryKiller引起的,还是其他原因。如果是LMK引起的,对在events里面打印am_low_memory:剩余进程数。wetest通过日志检测的方式,被测试应用进程退出后是否立马紧跟am_low_memeory来判断,进程退出由内存过低引起的。
///ActivityManagerService.java
final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread) {
.........
boolean doLowMem = app.instrumentationClass == null;
boolean doOomAdj = doLowMem;
if (!app.killedByAm) {
Slog.i(TAG, "Process " + app.processName + " (pid " + pid
+ ") has died");
mAllowLowerMemLevel = true;
} else {
// Note that we always want to do oom adj to update our state with the
// new number of procs.
mAllowLowerMemLevel = false;
doLowMem = false;
}
EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName);
if (DEBUG_CLEANUP) Slog.v(
TAG, "Dying app: " + app + ", pid: " + pid
+ ", thread: " + thread.asBinder());
handleAppDiedLocked(app, false, true);
if (doOomAdj) {
updateOomAdjLocked();
}
if (doLowMem) {
//判断是不是LMK
doLowMemReportIfNeededLocked(app);
}
......
其实低内存被杀对于游戏这种不能被中断,长期在作为前台应用的app来说,除了降低内存是没有比较好的途径的。降低内存是根本,但是内存很多时候并不是很好降低的。这个时候,我们需要其他措施来做预防。
另外,在QQ和微信登陆过程中游戏特别容易被杀。但是,手Q与微信在登陆成功之后,如果发现游戏挂了会重新拉起游戏。
这里说的最低值撇开流畅度,单值因为低内存闪退的情况。通过查看wetest平台几百台手机,最后/sys/module/lowmemorykiller/parameters/minfree在非root情况下有权限读取的为165台手机。前台应用最大阀值80MB,最低阀值8MB,平均阀值43.6MB。值得注意的是大部分新出的手机,均没有读取minfree的权限。
绝大多数手机集中在48MB:占比超过64.8%
最低手机为Vivo Y13:自身内存只有463M
最高手机为荣耀3X等:自身内存为2G
PS:对于内存过少的手机,可以在游戏开始时给用户提醒,降低用户这方面的预期。如果游戏在运行的最高峰时物理内存占比,会达到前台应用被杀的阀值,可能就会存在闪退风险。系统最大空闲内存-游戏最高内存<被杀阀值,即游戏的危险性。系统最大空闲内存>当前系统空闲内存,因为在运行过程中,低优先级的应用还会被杀,还会继续释放一部分内存。后续,可以对wetest平台所有手机做统计,看低优先级应用被杀后,Android系统内存空闲情况。
Application.onLowMemory是在4.0之前的一个接口,后台进程被杀完后回调一次。Application.onTrimMemory(int level)是在4.0之后提供的一个接口,任何实现了ComponentCallbacks2接口的类都可以重写实现这个回调方法。OnTrimMemory的主要作用就是 指导应用程序在不同的情况下进行自身的内存释放,以避免被系统直接杀掉,提高应用程序的用户体验。
Application.onTrimMemory总共有7个等级,其中在运行过程中可能会收到的等级为3个:
PS:针对手游的性能优化,腾讯WeTest平台的Cube工具提供了基本所有相关指标的检测,为手游进行最高效和准确的测试服务,不断改善玩家的体验。目前功能还在免费开放中。
点击地址:http://wetest.qq.com/cube立即体验!
https://testerhome.com/topics/4097
https://developer.android.com/guide/components/processes-and-threads.html?hl=zh-cn
http://wiki.jikexueyuan.com/project/deep-android-v2/activity.html
http://www.codeceo.com/article/android-ontrimmemory-mem.html
https://developer.android.com/reference/android/content/ComponentCallbacks2.html
双11活动马上开启,我们准备了百万Q币等你来领,据说每一个单身技术男都缺一桶Q币去勾搭美术妹子!