前言:
目前咱们对“android内存管理”大致比较注重,大家都需要分析一些“android内存管理”的相关资讯。那么小编在网上收集了一些有关“android内存管理””的相关内容,希望我们能喜欢,各位老铁们一起来学习一下吧!序言
众所周知,内存优化可以说是性能优化中最重要的优化点之一,可以说,如果你没有掌握系统的内存优化方案,就不能说你对Android的性能优化有过多的研究与探索;本篇,将带领大家一起来系统地学习Android中的内存调度机制
可能有不少读者都知道,在内存管理上,JVM拥有垃圾内存回收的机制,自身会在虚拟机层面自动分配和释放内存,因此不需要像使用C/C++一样在代码中分配和释放某一块内存;Android系统的内存管理类似于JVM,通过new关键字来为对象分配内存,内存的释放由GC来回收
Android系统在内存管理上有一个 Generational Heap Memory模型,当内存达到某一个阈值时,系统会根据不同的规则自动释放可以释放的内存;即便有了内存管理机制,但是,如果不合理地使用内存,也会造成一系列的性能问题,比如 内存泄漏、内存抖动、短时间内分配大量的内存对象 等等;下面,我就先来谈谈Android的内存管理机制
内存泄漏
概念
内存泄漏:ML(Memory Leak),程序向系统申请分配内存后,在使用完毕后未能及时释放内存,导致该内存单元一直被 占据着,无法再次被使用,直到程序结束,这是内存泄漏;一次内存泄漏危害可以忽略,但积累下来就会产生严重的危害,最终会导致内存溢出
内存泄漏的分类
常发性内存泄漏:发生内存泄漏的代码会被多次执行,每次执行都会导致一块内存泄漏
偶发性内存泄漏:某块内存泄漏的代码只有在特定的情况下才会执行,在这种情况下发生的内存泄漏是偶发性的
(常发性内存泄漏和偶发性内存泄漏是相对的,在某些情况下常发性可能变为偶发性,偶发性可能变为常发性,因此,测试环境和测试方法就显得尤为重要)
一次性内存泄漏:发生内存泄露的代码只会被执行一次
隐式内存泄漏:程序在工作的过程中不断分配内存,但是直到程序结束的时候才释放内存,这种情况严格的来说不算内存泄漏,程序最终释放了所有内存。但是对于长时间运行的程序,还是会耗尽系统的内存
内存泄漏的作用
内存泄漏是指程序在申请内存后,被某个对象一直持有而无法释放已申请的内存空间;内存泄漏不断堆积,应用程序可申请的内存空间就会越来越少,最终可能就出现,当程序片段被执行申请新的内存空间而不得,最终导致内存溢出
内存泄漏是因,内存溢出是果;针对于内存溢出,除了手机内存小,应用程序本身申请的大对象内存多(比如没有合理的处理bitmap),内存泄漏是导致内存溢出的一个重要的原因
故,在做我们的应用程序的内存优化的时候,内存溢出排查也是其中的一个重要方面。不少人认为JAVA程序,因为有垃圾回收机制,应该没有内存泄露
内存分配
在Android系统中,堆实际上就是一块匿名共享内存;Android虚拟机仅仅只是把它封装成一个 mSpace,由底层C库来管理,并且仍然使用libc提供的函数malloc和free来分配和释放内存
大多数静态数据会被映射到一个共享的进程中。常见的静态数据包括Dalvik Code、app resources、so文件等等
在大多数情况下,Android通过显示分配共享内存区域(如Ashmem或者Gralloc)来实现动态RAM区域能够在不同进程之间共享的机制;例如,Window Surface在App和Screen Compositor之间使用共享的内存,Cursor Buffers在Content Provider和Clients之间共享内存
上面说过,对于Android Runtime有两种虚拟机,Dalvik 和 ART,它们分配的内存区域块是不同的,下面我们就来简单了解下
Dalvik
● Linear Alloc
● Zygote Space
● Alloc Space
ART
● Non Moving Space
● Zygote Space
● Alloc Space
● Image Space
● Large Obj Space
不管是Dlavik还是ART,运行时堆都分为 LinearAlloc(类似于ART的Non Moving Space)、Zygote Space 和 Alloc Space
Dalvik中的Linear Alloc是一个线性内存空间,是一个只读区域,主要用来存储虚拟机中的类,因为类加载后只需要只读的属性,并且不会改变它。把这些只读属性以及在整个进程的生命周期都不能结束的永久数据放到线性分配器中管理,能很好地减少堆混乱和GC扫描,提升内存管理的性能
Zygote Space在Zygote进程和应用程序进程之间共享,Allocation Space则是每个进程独占。Android系统的第一个虚拟机由Zygote进程创建并且只有一个Zygote Space。但是当Zygote进程在fork第一个应用程序进程之前,会将已经使用的那部分堆内存划分为一部分,还没有使用的堆内存划分为另一部分,也就是Allocation Space。但无论是应用程序进程,还是Zygote进程,当他们需要分配对象时,都是在各自的Allocation Space堆上进行
当在ART运行时,还有另外两个区块,即 ImageSpace和Large Object Space
● Image Space:存放一些预加载类,类似于Dalvik中的Linear Alloc;与Zygote Space一样,在Zygote进程和应用程序进程之间共享
● Large Object Space:离散地址的集合,分配一些大对象,用于提高GC的管理效率和整体性能
注意:Image Space的对象只创建一次,而Zygote Space的对象需要在系统每次启动时,根据运行情况都重新创建一遍
内存回收机制
在Android的高级系统版本中,针对Heap空间有一个Generational Heap Memory的模型,其中将整个内存分为三个区域:
● Young Generation(年轻代)
● Old Generation(年老代)
● Permanent Generation(持久代)
模型示意图如下所示:
Young Generation
由一个Eden区和两个Survivor区组成,程序中生成的大部分新的对象都在Eden区中,当Eden区满时,还存活的对象将被复制到其中一个Survivor区,当此Survivor区满时,此区存活的对象又被复制到另一个Survivor区,当这个Survivor区也满时,会将其中存活的对象复制到年老代
Old Generation
一般情况下,年老代中的对象生命周期都比较长
Permanent Generation
用于存放静态的类和方法,持久代对垃圾回收没有显著影响
内存对象的处理过程小结
● 对象创建后在Eden区
● 执行GC后,如果对象仍然存活,则复制到S0区
● 当S0区满时,该区域存活对象将复制到S1区,然后S0清空,接下来S0和S1角色互换
● 当第3步达到一定次数(系统版本不同会有差异)后,存活对象将被复制到Old Generation
● 当这个对象在Old Generation区域停留的时间达到一定程度时,它会被移动到Old Generation,最后累积一定时间再移动到Permanent Generation区域
系统在Young Generation、Old Generation上采用不同的回收机制;每一个Generation的内存区域都有固定的大小;随着新的对象陆续被分配到此区域,当对象总的大小临近这一级别内存区域的阈值时,会触发GC操作,以便腾出空间来存放其他新的对象
此外,执行GC占用的时间与Generation和Generation中的对象数量有关,如下所示:
● Young Generation < Old Generation < Permanent Generation ● Generation中的对象数量与执行时间成反比
Young Generation GC
由于其对象存活时间短,因此基于Copying算法(扫描出存活的对象,并复制到一块新的完全未使用的控件中)来回收;新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在Young Generation区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC
6Old Generation GC
由于其对象存活时间较长,比较稳定,因此采用Mark(标记)算法(扫描出存活的对象,然后再回收未被标记的对象,回收后对空出的空间要么合并,要么标记出来便于下次分配,以减少内存碎片带来的效率损耗)来回收
内存溢出:是指程序代码片段被执行申请内存时,没有足够的内存空间,导致OOM异常
OOM:即内存溢出,out of momery
Android系统为每一个应用程序申请的内存是有限的(为什么如此设计,在dalvik虚拟机章节已经说明),一般为64M或者128M等,国内手机产商修改rom后,也会有所不同
我们可以在清单文件中配置android:largeheap=“true”,从而可给app申请更大的手机内存空间
总结
● 一个界面里的图片多而大的话,bitmap需要做缩放。(针对一进入该界面就内存溢出的情况)
● 少用全局的静态变量。用了要及时值空
● 列表需要及时清空
● 图片的全局缓存,用弱引用。保证内存不够的时候能GC
● View缓存需要及时清理
● 长生命周期里的对象不要引用短生命周期的对象(保证主与activity相关的对象能及时回收)
● 虚拟机只自动回收那些与root节点(主Activity,service)没有引用的对象
其实,要减小内存的使用,其实还有很多方法和要求。比如不要使用整张整张的图,尽量使用9path图片。Adapter要使用convertView等等,好多细节都可以节省内存。这些都需要我们去挖掘,谁叫Android的内存不给力来着
尾述
技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面
Android 架构师之路还很漫长,与君共勉
PS:有问题欢迎指正,可以在评论区留下你的建议和感受; 欢迎大家点赞评论,觉得内容可以的话,可以转发分享一下
标签: #android内存管理