当前位置:网站首页>【解惑】App处于前台,Activity就不会被回收了?
【解惑】App处于前台,Activity就不会被回收了?
2022-07-07 15:40:00 【QXXXD】
转换后的理解:单进程场景,Activity被回收只可能是因为进程被系统回收了。
感觉不太对?因为在很久以前,遇到过这样一个场景:
App打开多个Activity,然后手机晾一边,过一段时间后(屏幕常亮),点击回退,之前的Activity空白,然后重新加载了。
App在前台,不在栈顶的Activity却被干掉,但进程还健在,如果真是这样,就和上面的理解有些出入了。
立马写个代码验证下,大概流程如下:
写个父类Activity,生命周期回调加日志打印,接着打开一个Activity,包含一个按钮,点击后依次打开多个Activity,最后一个加个按钮,点一下就申请一个大一点的ByteArray来模拟内存分配,看内存不足时是否会回收Activity。
测试结果如下:
App宁愿OOM,也不愿意回收Activity,鬼使神差地加上 android:largeHeap=“true” ,结果一样。
em…难道是我记错了???
等等!!!我好像混淆了两个东西:系统可用内存不足
和 应用可用内存不足
。
0x1、系统可用内存不足
LMK机制
Android系统中,进程的生命周期由系统控制,处于体验和性能考虑,在APP中点击Home键或Back回退操作,并不会真的杀掉APP,进程依旧存在于内存中,这样下次启动此APP时就能更加快速。随着系统运行时间增长,打开APP越来越多,内存中的进程随着增多,系统的可用内存会越来越少。咋办,总不能让用户自己去杀进程吧,所以系统内置一套 回收机制,当系统可用内存达到一个 阈值,系统会根据 进程优先级 来杀掉一部分进程,释放内存供后续启动APP使用。
Android的这套回收机制,是基于Linux内核的OOM规则改进而来的,叫 Low Memory Killer
,简称 LMK。
阈值 & 杀谁
通过下述两个文件配合完成,不同手机数值可能不同,以我的老爷机 魅蓝E2 为例 (Android 11的Mix2S一直说没权限打开此文件):
# /sys/module/lowmemorykiller/parameters/minfree
# 单位:Page页,1Page = 4KB
18432,23040,27648,46080,66560,97280
# /sys/module/lowmemorykiller/parameters/adj
0,58,117,176,529,1000
Android系统会为每个进程维护一个 adj(优先级)
:
- Android 6及以前称为:
oom_adj
,值范围:[-17,16],LMK要换算*1000/17 - Android 7后称为:
oom_score_adj
,值范围:[-1000,1000]
然后,上面两个文件的值,其实是以一一对应的,比如:
66560 * 4 / 1024 = 260MB → 当系统可用内存减少到260MB时,会杀掉adj值大于529的进程;
18432 * 4 / 1024 = 72MB → 当系统可用内存减少到72MB,杀掉ajd值大于0的进程;
adj怎么看
直接通过命令行查看:
可以看到,adj是动态变化的,当App状态及四大组件生命周期发生改变时,都会改变它的值。常见ADJ级别如下:
- NATIVE_ADJ → -1000,init进程fork出来的native进程,不受system管控;
- SYSTEM_ADJ → -900,system_server进程;
- PERSISTENT_PROC_ADJ → -800,系统persistent进程,一般不会被杀,杀了或者Carsh系统也会重新拉起;
- PERSISTENT_SERVICE_ADJ → -700,关联着系统或persistent进程;
- FOREGROUND_APP_ADJ → 0,前台进程;
- VISIBLE_APP_ADJ → 100,可见进程;
- PERCEPTIBLE_APP_ADJ → 200,可感知进程,比如后台音乐播放;
- BACKUP_APP_ADJ → 300,执行bindBackupAgent()过程的备份进程;
- HEAVY_WEIGHT_APP_ADJ → 400,重量级进程,system/rootdir/init.rc文件中设置;
- SERVICE_ADJ → 500,服务进程;
- HOME_APP_ADJ → 600,Home进程,类型为ACTIVITY_TYPE_HOME的应用,如Launcher;
- PREVIOUS_APP_ADJ → 700,用户上一个使用的App进程;
- SERVICE_B_ADJ → 800,B List中的Service;
- CACHED_APP_MIN_ADJ → 900,不可见进程 的adj最小值;
- CACHED_APP_MAX_ADJ → 906,不可见进程的adj最大值;
- UNKNOWN_ADJ → 1001,一般指将要会缓存进程,无法获取确定值;
关于ADJ计算的详细算法分析可见Gityuan大佬的:《解读Android进程优先级ADJ算法》,干货多多,顺带从总结处捞一波进程保活伎俩:
- UI进程与Service进程分离,包含Activity的Service进程,一进后台ADJ>=900,随时可能被系统回收,分离的话ADJ=500,被杀的可能性降低,尤其是系统允许自启动的服务进程,必须做UI分离,避免消耗较大内存;
- 真正需要用户可感知的应用,调用startForegroundService()启用前台服务,ADJ=200;
- 进程中的Service工作完,务必主动调用stopService或stopSelf来停止服务,避免占用内存,浪费系统资源;
- 不要长时间绑定其他进程的service或者provider,每次使用完成后应立刻释放,避免其他进程常驻于内存;
- APP应该实现接口onTrimMemory()和onLowMemory(),根据TrimLevel适当地将非必须内存在回调方法中加以释放,当系统内存紧张时会回调该接口,减少系统卡顿与杀进程频次;
- 更应在优化内存上下功夫,相同ADJ级别,系统会优先杀内存占用的进程;
问:能否把自己的App的ADJ值设置为-1000,让其杀不死? 答:不可以,要有root权限才能修改adj,而且改了重启手机还是恢复的。
扯得有点远了,回到问题上:
系统内存不足时,会在内核层直接查杀进程,不会在Framework层还跟你叨逼叨看回收哪个Activity。
所以在系统这个层面,单进程场景,Activity被回收只可能是因为进程被系统回收了,这句话是没毛病的,但在应用层面就不一定了。
0x2、应用可用内存不足
APP进程(虚拟机)的内存分配实际上是对 堆的分配和释放,为了整个系统的内存控制需要,会为每个应用程序设置一个 堆的限制阈值,如果应用使用内存接近阈值还尝试分配内存,就很容易引起OOM。
当然,不会那么蠢,还要开发仔自己在APP里回收内存,虚拟机自带 GC,这里就不向去卷具体的回收算法了
假设应用内存不足真的会回收Activity,那该怎么设计?一种解法如下:
应用启动时,开一个子线程,定时轮询当前可用内存是否超过阈值,超过的话干掉Activity
那就来跟下Android是不是也是这样设计的?
Activity回收机制
跟下应用启动入口:ActivityThread → main()
跟下 attach():
这里就非常像,run()中计算:已用内存 > 3/4最大内存,就执行 releaseSomeActivities(),跟下:
所以 getService() 是获取了 IActivityTaskManager.aidl接口,具体的实现类是 ActivityTaskManangerService:
继续往下跟: RootActivityContainer → releaseSomeActivitiesLocked():
跟下:WindowProcessController → getReleaseSomeActivitiesTasks()
然后再往下走就是释放Activity的代码了:ActivityStack → releaseSomeActivitiesLocked()
具体咋释放,就不往下跟了哈,接着跟下是怎么监控的~
内存监控机制
跟回:BinderInternal.addGcWatcher()
这里可能看得你有点迷,但是当你理解了就会觉得很妙了:
虚拟机GC会干掉 WeakReference 的对象,在释放内存前,会调用对象的 finalize(),而这里有创建了一个新的 WeakReference 实例。下次GC,又会走一遍这里的代码,啧啧啧,相比起轮询高效多了
到此,应用内存不足回收Activity的流程就大概缕清了,接着可以写个代码验证下是否真的这样。
Demo验证
先试下两个Task的:
模拟内存分配的页面,然后一直点~
宁愿OOM,也不回收,试试三个~
好家伙,onDestory()了,此时按Back回退这些页面,发现走了onCreate(),即回收了,接着试试四个的情况:
可以,每次只回收一个Task,到此验证完毕了~
0x3、结论
- 系统内存不足时,直接在内核层查杀(回收)进程,并不会考虑回收哪个Activity;
- 进程内存不足时,如果此进程 Activity Task数 >= 3 且 使用内存超过3/4,会对 不可见 Task进行回收,每次回收 1个 Task,回收时机为每次gc;
文末
我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。
需要的直接点击文末小卡片可以领取哦!我免费分享给你,以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持,需要的自己领取)
Android学习PDF+架构视频+面试文档+源码笔记
部分资料一览:
- 330页PDF Android学习核心笔记(内含8大板块)
- Android学习的系统对应视频
- Android进阶的系统对应学习资料
- Android BAT大厂面试题(有解析)
边栏推荐
- 第2章搭建CRM项目开发环境(数据库设计)
- viewflipper的功能和用法
- LeetCode 648(C#)
- 【TPM2.0原理及应用指南】 5、7、8章
- Sator推出Web3遊戲“Satorspace” ,並上線Huobi
- 浅谈 Apache Doris FE 处理查询 SQL 源码解析
- Flash build API Service - generate API documents
- SIGGRAPH 2022最佳技术论文奖重磅出炉!北大陈宝权团队获荣誉提名
- DevOps 的运营和商业利益指南
- User defined view essential knowledge, Android R & D post must ask 30+ advanced interview questions
猜你喜欢
命令模式 - Unity
PLC:自动纠正数据集噪声,来洗洗数据集吧 | ICLR 2021 Spotlight
Linux 安装mysql8.X超详细图文教程
SlashData开发者工具榜首等你而定!!!
Several best practices for managing VDI
Is AI more fair than people in the distribution of wealth? Research on multiplayer game from deepmind
【TPM2.0原理及应用指南】 9、10、11章
What is cloud computing?
【分布式理论】(二)分布式存储
《世界粮食安全和营养状况》报告发布:2021年全球饥饿人口增至8.28亿
随机推荐
ViewSwitcher的功能和用法
How to implement safety practice in software development stage
99% of users often make mistakes in power Bi cloud reports
使用Stace排除故障的5种简单方法
viewflipper的功能和用法
Biped robot controlled by Arduino
rpcms获取指定分类下的文章的方法
【网络攻防原理与技术】第1章:绪论
imageswitcher的功能和用法
MySQL implements the query of merging two fields into one field
智慧物流平台:让海外仓更聪明
【网络攻防原理与技术】第3章:网络侦察技术
Nerf: the ultimate replacement for deepfake?
Smart logistics platform: make overseas warehouses smarter
[video / audio data processing] Shanghai daoning brings you elecard download, trial and tutorial
SlashData开发者工具榜首等你而定!!!
无法链接远程redis服务器(解决办法百分百)
【饭谈】Web3.0到来后,测试人员该何去何从?(十条预言和建议)
[fan Tan] those stories that seem to be thinking of the company but are actually very selfish (I: building wheels)
管理VDI的几个最佳实践