APP管理应用的内存
16lz
2021-01-26
编写: kesenhoo - 原文: http://developer.android.com/training/articles/memory.html Random Access Memory(RAM)在任何软件开发环境中都是一个很宝贵的资源。这一点在物理内存通常很有限的移动操作系统上,显得尤为突出。尽管 Android的Dalvik虚拟机 扮演了常规的垃圾回收的角色,但这并不意味着你可以忽视app的内存分配与释放的时机与地点。 为了GC能够从app中及时回收内存,我们需要注意避免内存泄露(通常由于在全局成员变量中持有对象引用而导致)并且在适当的时机(下面会讲到的lifecycle callbacks)来释放引用对象。对于大多数app来说,Dalvik的GC会自动把离开活动线程的对象进行回收。 这篇文章会解释Android是如何管理app的进程与内存分配,以及在开发Android应用的时候如何主动的减少内存的使用。关于Java的资源管理机制,请参考其它书籍或者线上材料。如果你正在寻找如何分析你的内存使用情况的文章,请参考这里 Investigating Your RAM Usage 。 第1部分: Android是如何管理内存的 Android 并没有为内存提供 交换区(Swap space),但是它有使用 paging 与 memory-mapping(mmapping) 的机制来管理内存。这意味着 任何你修改的内存(无论是通过分配新的对象还是去访问mmaped pages中的内容) 都会贮存在RAM中,而且不能被置换出去(paged out)。因此唯一完整释放内存的方法是释放那些你可能hold住的对象的引用,当这个对象没有被任何其他对象所引用的时候,它就能够被GC回收了。只有一种例外是:如果系统想要在其他地方重用这个对象。 1) 共享内存 Android通过 下面几个方式在不同的进程中 来实现共享RAM:
- 每一个app的进程都是从一个被叫做Zygote的进程中fork出来的。Zygote进程在系统启动并且载入通用的framework的代码与资源之后开始启动。为了启动一个新的程序进程,系统会fork Zygote进程生成一个新的进程,然后在新的进程中加载并运行app的代码。这使得大多数的RAM pages被用来分配给framework的代码,同时使得RAM资源能够在应用的所有进程中进行共享。
- 大多数static的数据被mmapped到一个进程中。这不仅仅使得同样的数据能够在进程间进行共享,而且使得它能够在需要的时候被paged out。例如下面几种static的数据:
- Dalvik 代码 (放在一个预链接好的 .odex 文件中以便直接mapping)
- App resources (通过把资源表结构设计成便于mmapping的数据结构,另外还可以通过把APK中的文件做aligning的操作来优化)
- 传统项目元素,比如 .so 文件中的本地代码.
- 在很多情况下,Android通过显式的分配共享内存区域(例如ashmem或者gralloc)来实现一些动态RAM区域能够在不同进程间进行共享。例如,window surfaces在app与screen compositor之间使用共享的内存,cursor buffers在content provider与client之间使用共享的内存。
- 每一个进程的Dalvik heap都有一个受限的虚拟内存范围。这就是逻辑上讲的heap size,它可以随着需要进行增长,但是会有一个系统为它所定义的上限。
- 逻辑上讲的heap size和实际物理上使用的内存数量是不等的,Android会计算一个叫做Proportional Set Size(PSS)的值,它记录了那些和其他进程进行共享的内存大小。(假设共享内存大小是10M,一共有20个Process在共享使用,根据权重,可能认为其中有0.3M才能真正算是你的进程所使用的)
- Dalvik heap与逻辑上的heap size不吻合,这意味着Android并不会去做heap中的碎片整理用来关闭空闲区域。Android仅仅会在heap的尾端出现不使用的空间时才会做收缩逻辑heap size大小的动作。但是这并不是意味着被heap所使用的物理内存大小不能被收缩。在垃圾回收之后,Dalvik会遍历heap并找出不使用的pages,然后使用madvise(系统调用)把那些pages返回给kernal。因此,成对的allocations与deallocations大块的数据可以使得物理内存能够被正常的回收。然而,回收碎片化的内存则会使得效率低下很多,因为那些碎片化的分配页面也许会被其他地方所共享到。
- TRIM_MEMORY_RUNNING_MODERATE:你的app正在运行并且不会被列为可杀死的。但是设备此时正运行于低内存状态下,系统开始触发杀死LRU Cache中的Process的机制。
- TRIM_MEMORY_RUNNING_LOW:你的app正在运行且没有被列为可杀死的。但是设备正运行于更低内存的状态下,你应该释放不用的资源用来提升系统性能(但是这也会直接影响到你的app的性能)。
- TRIM_MEMORY_RUNNING_CRITICAL:你的app仍在运行,但是系统已经把LRU Cache中的大多数进程都已经杀死,因此你应该立即释放所有非必须的资源。如果系统不能回收到足够的RAM数量,系统将会清除所有的LRU缓存中的进程,并且开始杀死那些之前被认为不应该杀死的进程,例如那个包含了一个运行态Service的进程。
- TRIM_MEMORY_BACKGROUND: 系统正运行于低内存状态并且你的进程正处于LRU缓存名单中最不容易杀掉的位置。尽管你的app进程并不是处于被杀掉的高危险状态,系统可能已经开始杀掉LRU缓存中的其他进程了。你应该释放那些容易恢复的资源,以便于你的进程可以保留下来,这样当用户回退到你的app的时候才能够迅速恢复。
- TRIM_MEMORY_MODERATE: 系统正运行于低内存状态并且你的进程已经已经接近LRU名单的中部位置。如果系统开始变得更加内存紧张,你的进程是有可能被杀死的。
- TRIM_MEMORY_COMPLETE: 系统正运行与低内存的状态并且你的进程正处于LRU名单中最容易被杀掉的位置。你应该释放任何不影响你的app恢复状态的资源。
- Enums的内存消耗通常是static constants的2倍。你应该尽量避免在Android上使用enums。
- 在Java中的每一个类(包括匿名内部类)都会使用大概500 bytes。
- 每一个类的实例花销是12-16 bytes。
- 往HashMap添加一个entry需要额一个额外占用的32 bytes的entry对象。
更多相关文章
- Android中两种设置全屏或者无标题的方法
- Android(安卓)CPU, Compilers, D8 & R8
- Android(安卓)自定义发送验证码, 倒计时
- WakeLock使用方法示例代码
- [代码片段] 【转】Android以最省内存的方式读取本地资源的
- Android通过LIstView显示文件列表的两种方法介绍
- GLSurfaceView 基本使用与源码解析
- android模仿移动MM Tab 点击 背景 滑动效果
- [置顶] Android系统移植与调试之------->build.prop生成过程分析