2018-08-08 13:28:27 486浏览
Android是一个基于Linux实现的操作系统。但对于Linux内核来说,Android也仅仅只是一个运行在内核之上的应用程序,与其他运行在内核之上的应用程序没有任何区别。所以Android需要一套机制管理运行在Linux进程中的APK应用程序。很多参加Android培训的同学也需要知道这个知识点。Android内存管理包含两部分,一部分是Framework对内存的管理,一部分是Linux内核对内存管理,这两部分共同决定应用程序的生命周期。本文主要阐述Android内存管理机制的实现原理,以及在应用开发中需要注意的一些事项,最后文章总结了如何实现杀不死进程的一种方法。本文分为两部分阅读。
Linux进程回收
在Android中,大部分应用程序都运行在一个独立的Linux进程中,每个进程都有独立的内存空间。随着各种应用程序启动,系统内存不断下降,为了保证新应用能够运行,Android需要一套机制杀死暂时闲置的进程。
AndroidFramework并不能直接回收内存,其管理进程的服务(ActivityManagerService,以下简称AmS)也同应用程序一样运行在Java虚拟机环境里。Java虚拟机都运行在各自独立的内存空间,所以ActivityManagerService没有办法感知应用程序是否OOM。
Android系统中还运行了一个OOM进程。该进程启动时首先会在Linux内核中把自己注册为一个OOMKiller。AmS需要把每一个应用程序的oom_adj值告知OOMKiller,这个值的范围在-16到15之间,值越低,说明越重要,这个值类似于Linux中的nice值,只在标准的Linux中,有其自己的OOMKiller。Android中的OOMKiller进程仅仅适用于Android应用程序。
当内核的内存管理模块检测到系统内存不足时就会通知OOMKiller,然后OOMKiller根据AmS所告知的优先级强制退出优先级低的应用程序。
应用程序在内存中的状态
Android官方声称,Activity退出后,其所在进程并不会被立即杀死,从而在下次启动Activity时,能够提高启动速度。这些Activity只有在内存紧张时才会被系统杀死。所以对于应用程序来说,关闭并不意味着释放内存。
Activity在内存中的状态
系统只有一个Activity处于与用户交互的状态,对于非交互状态的Activity,AmS会在内部暂时缓存起来而不是立即杀死,但如果后台Activity数目超过一定阈值,AmS则会强制杀死一些优先级低的Activity。以下是Activity在内存或者说在AmS中的状态:
AmS会记录最近启动的20个Activity,如果超过20则舍弃最早记录的Activity。
AmS会将所有正在运行的Activity保存在一个列表中,对于使用back返回的Activity则从列表中清除。
AmS使用Lru算法保存所有最近使用过的Activity。
AmS使用一个列表(mStoppingActivities)保存需要停止的Activity,这种情况
发生在启动一个Activity时,AmS遵循先启动后停止的策略,将需要停止的Activity保存在此列表中,等AmS闲置下来后再停止Activity。
AmS使用一个列表保存处于finish状态(onDestory())的Activity,当一个Activity处于finish状态时(onDestory()执行后)不会被立即杀死,而是保存到该列表中直到超过系统设定的警戒线才会回收该列表中的Activity。
应用进程在内存中的状态
每个应用程序都对应着一个ActivityThread类,该类初始化后就进入Looper.loop()函数中无限循环。
Looper.prepareMainLooper();
...
ActivityThreadthread=newActivityThread();
thread.attach(false);
...
Looper.loop();
以后则依靠消息机制运行,既当有消息时处理消息,没有消息则应用进程进入sleep状态。loop()方法内部代码如下所示:
publicstaticfinalloop(){
Looperme=myLooper();
MessageQueuequeue=me.mQueue;while(true){
Messagemsg=queue.next();//mightblock
...
}
}
在Linux内核调度中,如果一个线程的状态为sleep,则除了占用调度本身的时间,不会占用CPU时间片。
有三种情况会唤醒应用线程,一种是定时器中断(比如我们设置的闹钟,在程序中可以设置定时任务),第二种是用户按键消息,第三种是Binder消息(Binder用于进程间通信,其在应用程序中会自动创建一个线程,Binder在接收到消息后会想UI主线程发送一个消息从而使queue.next()继续执行)这就是所谓的消息驱动模式。
所以设计良好的应用程序当处于后台时不会占用任何CPU时间,更不会拖慢系统运行速度。其所占用的仅仅是内存,即使释放所占用的内存也不会提高系统运行速度。当然这里说的是设计良好的应用程序,目前国内很多应用在处于后台状态时依然会偷偷干很多事情,这无疑就拖慢了系统运行速度。
Android内存回收
Activity所占内存在一般情况下不会被回收,只有在系统内存不够用时才会回收,并且回收会遵循一定规则。大致可以概括为前台Activity最后回收,其次是包含前台的Service或者Provider,再其次是后台Activity,最后是空进程。
内存释放的三个地方
第一个是在ActivityManagerService中运行,即Android所声称的当系统内存低时,优先释放没有任何Activity的进程,然后释放非前台Activity对应的进程。
第二个是在OOMKiller中,此时AmS只要告诉OOM各个应用的优先级,然后OOM就会调用Linux内部的进程管理方法杀死优先级较低的进程。
第三个是在应用进程本身之中,当AmS认为目标进程需要被杀死时,首先会通知目标进程进程内存释放。这包括调用目标进程的scheduleLowMemory()方法和processInBackground()方法。
关闭Activity的三种情况
第一种,从调用startActivity()开始,一般情况下,当前都有正在运行的Activity,所以需要先暂停当前的Activity,而暂停完毕后,AmS会收到一个Binder消息,并开始从completePaused()处执行。在该函数中,由于上一个Activity并没有finishing,仅仅是stop,所以这里会把上一个Activity添加到mStoppingActivity列表中。当目标Activity启动后,会向Ams发送一个请求进行内存回收的消息,这会导致AmS在内部调用activityIdleInternal()方法,该方法中首先会处理mStoppingActivities列表中的Activity,这就会调用stopActivityLocked()方法。这又会通过IPC调用,通知应用进程stop指定的Activity,当stop完毕后,再报告给AmS,于是AmS再从activityStopped()出开始执行,而这会调用trimApplication()方法,该方法会执行内存相关的操作。
第二种,当按Back键后,会调用finishActivityLocked(),然后把该Activity的finishing标识设为true,然后再调用startPausingLocked(),当目标Activity完成暂停后,就会报告AmS,此时AmS又会从completePaused()处开始执行。与第一种情况不同,由于此时暂停的Activity的finishing状态已经设置为true,所以会执行finishingActivityLocked(),而不是像第一种情况中仅仅把该Activity添加到mStoppingActivities列表。
第三种,当Activity启动后,会向AmS发送一个Idle消息,这会导致AmS开始执行activityIdleInternal()方法。该方法会首先处理mStoppingActivities列表中的对象,接着处理mFinishingActivities列表,最后再调用trimApplication()方法。
以上就是关闭Activity的三种情况,包括stop和destory,客户进程中与之对应的就是onStop()和onDestory()的调用。
如果使用OOM还有AmS机制杀死后台进程后,此时运行的Activity数量依然超过MAX_ACTIVITIES(20),则需要继续销毁满足以下三个条件的Activity:
Activity必须已经stop,但却没有finishing
必须是不可见的,既该Activity窗口上面有其他全屏的窗口,如果不是全屏,则后面的Activity是可见的。
不能是persistent类型,既常驻进程不能被杀死。
以上这篇就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持扣丁学堂,想要了解更多详情请登录扣丁学堂官网咨询或者关注微信公众号,里面有最新的扣丁学堂
Android视频教程等你来看!
【关注微信公众号获取更多学习资料】