转发Android内存管理机制
这篇文章讲解的很详细,我进行了简单总结。
线程私有:程序计数器,虚拟机栈,本地方法栈
线程共享:堆,方法区
程序计数器 :当前线程所指向的字节码指示器,Java方法存放虚拟机字节码指令地址,Native方法,计数器值为Undefined,唯一不存在OOM
虚拟机栈:当线程每个方法执行时都创建一个栈帧用来存放局部变量,方法出口等,并将该栈帧放在JVM栈中。如果栈深度大于虚拟机允许最大深度,抛出StackOverFlowError,不过虚拟机基本都允许动态扩展虚拟机栈大小,这样一直申请会抛出OOM
本地方法栈:存放的栈帧是Native方法调用时产生的
堆:对象存储地方,GC重要区域
方法区:静态区,用于存储类信息、常量池等。方法区可以选择是否开启垃圾回收。JVM内存不足会抛出OOM。有永久代时,方法区被用作有永久代
标记所有需要回收对象,标记后统一回收。
效率不高,会产生大量不连续内存碎片
内存划分两块,一块内存用完复制到另一个,清理另一块内存。
只标记2分之一所以简单高效,但是浪费一半空间
标记需回收内存,存活对象往一端移动,清理剩余内存。
避免内存碎片和空间浪费,比较合理
安卓选取该算法,分为年轻代,老年代,永久代(JDK1.8后取消)
年轻代:回收频率高,采用标记-复制,eden和SO,S1 三个区 8:1:1 分配,eden区满的时候存活对象复制到Survivor区,以此类推,从第一个Survior复制过来此时还存活,将被复制到老年代
老年代:相对稳定但存活对象多,采用标记-整理
永久代:存放静态类和方法,GC对这没有显著影响
三级模型中,每个区域都有大小固定的值,当达到某一级内存区域阈值就触发GC
Android系统为每个应用设置了硬性Dalvik Heap Size最大限制阈值,RAM不同阈值设定不同。只有allocated + 新分配内存 >= getMemoryClass 发生OOM。
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
int value = am.getMemoryClass();
一个线程可以理解为一个根节点,里面是有向图的树,根顶点可到达的对象都是有效对象,反之就说明没有被 引用可以GC,Java使用有向图管理可以消除循环问题,精度高,效率低。另一种常用内存管理技术使用计数器,例如COM模型采用计数器方式,精度低,效率高。
JVM提供GC策略也很多,有平缓执行GC,有中断执行GC,定时执行GC,或者内存到达一定程度GC
1.前台进程(正常不会被杀死)
与用户交互Activity,Service绑定正在交互Activity,onReceive方法的BroadcastReceiver,正在执行生命周期的Service
2.可见进程(正常不会被杀死)
前台仍可见的Activity onPause方法,比如弹框时
Service.startForeground启动前台Service ,或者特定Service,动态壁纸
3.服务进程(正常不会杀死)
启动的startService ,例如网络上传,下载数据,除非内存不足一般不会杀死
4.后台进程(随时杀死)
这类进程持有一个多个不可见Activity,可以随时回收,但例如Activity onSaveInstance即使杀死也能正常重启
5.空进程(随时杀死)
不含活动应用组件进程,保留这种进程的的唯一目的是用作缓存,缩短下次运行组件启动时间,
Linux内核算法采用打分(oom_score),oom_score_adj -1000到1000 ,数值越大级别越低,越容易杀死。普通app进程 >=0,系统的才可能小于0
那么系统什么时候更新 oom_score_adj ?
启动一个Activity或Activity前台转后台,状态变化时更新。
AMS有两个方法更新:
final boolean updateOomAdjLocked(ProcessRecord app) 针对单进程更新优先级
final void updateOomAdjLocked() 对所有进程更新优先级
系统什么时候根据 oom_score_adj 杀进程?
AMS 调用updateOomAdjLocked() 判断进程是否需要被杀死,若是则调用ProceeRecord::kill()杀死进程