对于 Android 设备来说,我们每打开一个 APP,它的内存都是弹性分配的,并且其分配值与最大值是受具体设备而定的。
此外,我们需要注意区分如下两种 OOM 场景:
- 1)、内存真正不足:例如 APP 当前进程最大内存上限为 512 MB,当超过这个值就表明内存真正不足了。
- 2)、可用内存不足:手机系统内存极度紧张,就算 APP 当前进程最大内存上限为 512 MB,我们只分配了 200 MB,也会产生内存溢出,因为系统的可用内存不足了。
在Android的高级系统版本中,针对Heap空间有一个Generational Heap Memory的模型,其中将整个内存分为三个区域:
- Young Generation(年轻代)
- Old Generation(年老代)
- Permanent Generation(持久代)
1、Young Generation
由一个Eden区和两个Survivor区组成,程序中生成的大部分新的对象都在Eden区中,当Eden区满时,还存活的对象将被复制到其中一个Survivor区,当此Survivor区满时,此区存活的对象又被复制到另一个Survivor区,当这个Survivor区也满时,会将其中存活的对象复制到年老代。
2、Old Generation
一般情况下,年老代中的对象生命周期都比较长。
3、Permanent Generation
用于存放静态的类和方法,持久代对垃圾回收没有显著影响。(在 JDK 1.8 及之后的版本,在本地内存中实现的元空间(Meta-space)已经代替了永久代)
4、内存对象的处理过程小结
- 1、对象创建后在Eden区。
- 2、执行GC后,如果对象仍然存活,则复制到S0区。
- 3、当S0区满时,该区域存活对象将复制到S1区,然后S0清空,接下来S0和S1角色互换。
- 4、当第3步达到一定次数(系统版本不同会有差异)后,存活对象将被复制到Old Generation。
- 5、当这个对象在Old Generation区域停留的时间达到一定程度时,它会被移动到Old Generation,最后累积一定时间再移动到Permanent Generation区域。
系统在Young Generation、Old Generation上采用不同的回收机制。每一个Generation的内存区域都有固定的大小。随着新的对象陆续被分配到此区域,当对象总的大小临近这一级别内存区域的阈值时,会触发GC操作,以便腾出空间来存放其他新的对象。
此外,执行GC占用的时间与Generation和Generation中的对象数量有关,如下所示:
- Young Generation < Old Generation < Permanent Generation
- Generation中的对象数量与执行时间成反比。
5、Young Generation GC
由于其对象存活时间短,因此基于Copying算法(扫描出存活的对象,并复制到一块新的完全未使用的控件中)来回收。新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在Young Generation区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。
6、Old Generation GC
由于其对象存活时间较长,比较稳定,因此采用Mark(标记)算法(扫描出存活的对象,然后再回收未被标记的对象,回收后对空出的空间要么合并,要么标记出来便于下次分配,以减少内存碎片带来的效率损耗)来回收。
7、Dalvik 与 ART 区别
- 1)、Dalivk 仅固定一种回收算法。
- 2)、ART 回收算法可运行期选择。
- 3)、ART 具备内存整理能力,减少内存空洞。
网友评论