经常在网上看到一些大神分享不少关于内存优化方面的建议,受益匪浅,打算从官方的文档学习一下内存管理的知识,自己翻译提升印象的同时分享给其他开发的小伙伴,希望能一起进步,有翻译或者理解不对的地方欢迎指正。原文地址: https://developer.android.com/training/articles/memory.html
RAM在所有的操作系统中都是稀缺资源,在手机中因为物理内存受到限制,这个问题还要严重得多。虽然Dalvik虚拟机负责垃圾回收,但是你不能因此而忽略内存的分配释放问题。
为了垃圾回收器可以从你应用中回收内存,你应该避免内存泄漏(通常由于全局成员引用了一个对象引起),在合适的时间释放对对象的引用(在生命周期回调中)。对于大多数应用来说,垃圾回收器负责:当对象离开了它的作用域时,系统就回收分配给它们的内存。
这篇文章解释了,安卓系统如何管理app的进程和内存分配释放和我们如何在开发应用中减少对内存的使用。更多关于使用JAVA清理资源的信息,我们可以参考其他书或者在线文档资源。如果你正在寻找如何分析自己app中已分配内存的方法,建议阅读read Investigating Your RAM Usage.送上地址:https://developer.android.com/studio/profile/investigate-ram.html
安卓如何管理内存
安卓没有给内存提供交换空间,它使用页、内存映射来管理内存。这意味着任何修改内存-无论是分配新对象,还是映射表,都会在RAM内存中常驻无法移除。所以唯一释放内存的方式就是移除对象的引用,这样垃圾回收器就可以释放内存了。这可能发生一个异常:任何在映射表中却没有修改的文件,比如代码,都可能被从RAM中移除。
共享内存
为了让RAM可以处理所有的事,安卓系统在进程间共享RAM它通过下面的方法实现:
每个应用进程都是从一个Zygote进程中分支fork出来的。当系统开始运行,加载通用框架代码和资源(比如activity主题)时,Zygote进程开始工作。为了开启一个新的应用进程, 系统fork Zygote进程产生一个新的进程,然后加载并运行代码。这就使得大多数的RAM 页被用来分配给framework的代码,同时促使RAM资源能够在应用的所有进程之间进行共享。
大多数static数据被映射到一个进程中。这不仅使得同样的数据能够在进程间共享,也能在有需要的时候被释放。常见的static数据:Dalvik虚拟机代码,应用资源和.so文件等。
在多数情况下,安卓通过显式分配内存区域(例如ashmem和gralloc)在进程之间共享动态RAM资源。比如,Window Surface在App与Screen Compositor之间使用共享的内存,cursor buffers在content provider和客户端之间共享内存。
分配和回收内存
安卓如何分配和回收内存:
Dalvik虚拟机分配给每个进程的heap堆内存被限制在虚拟内存范围内。这定义了逻辑的堆内存的大小,根据需要这个大小会自增长(但每个应用最多只能增长到一个上限)。
逻辑上的堆内存大小和实际物理上使用的内存大小是不一样的。当检查应用的堆栈时,安卓会计算一个称为Proportional Set Size(PSS)的值,记录了应用程序自身占用以及与其他进程进行共享的内存。这个PSS值的总大小才被系统认为是物理内存的大小。更多关于PSS的信息,请查看Investigating Your RAM Usage。
Dalvik 不会对堆内存中的空闲内存做内存整理。只有在Heap的尾部有没有足够的内存空间时,才会触发垃圾释放GC。GC后,Dalvik 会检查Heap,找到无用的页,通过madvise函数把它们返回给内核。因此,成对申请和释放大片内存会导致所有使用过的物理内存被回收。然而,回收小块内存可能效率更差,因为小块内存使用的页可能仍被还没释放的对象占用,导致该page无法回收。
限制应用内存
为了维持多任务处理环境,安卓系统对每个应用的heap大小都进行了严格的限制。这个数值随着设备变化而变化,具体要看设备可用的RAM空间有多少。一旦你的应用占用的内存接近了堆栈上限,却仍申请更多内存,你将收到OOM错误提示。
你可以通过调用getMemoryClass()方法得到应用可用的堆栈大小。这将会在下面的篇幅讨论,详见Check how much memory you should use.
应用间的切换
当用户切换应用时,安卓并不会交换内存空间,而是把非前台进程放到LRU缓存中。例如,当用户第一次启动一个应用时,系统会为它开启一个进程。但是当用户离开这个应用时,进程不会立即被销毁,而是被缓存到系统中,因此到用户返回应用时,进程会reused,以便加快应用切换。
如果你的应用有一个被缓存的进程,这个被缓存的进程也会占用一定的内存,即便用户不再使用你的应用,它会对系统的整体性能有影响。因此,当系统内存不足,它会杀掉LRU缓存中最近没用过的进程,但也会考虑优先杀死占用内存最多的进程。