Android中的内存管理机制以及正确的使用方式

概述

从操作系统的角度来说,内存就是一块数据存储区域,属于可被操作系统调度的资源。现代多任务(进程)的操作系统中,内存管理尤为重要,操作系统需要为每一个进程合理的分配内存资源,所以可以从两方面来理解操作系统的内存管理机制。

第一:分配机制。为每一个进程分配一个合理的内存大小,保证每一个进程能够正常的运行,不至于内存不够使用或者每个进程占用太多的内存。

第二:回收机制。在系统内存不足打的时候,需要有一个合理的回收再分配的机制,以保证新的进程可以正常运行。回收的时候就要杀死那些正在占有内存的进程,操作系统需要提供一个合理的杀死这些进程的机制,以保证更少的副作用。

而作为一个多进程的操作系统,Android系统对内存的管理,也是有一套自己的方法的。跟PC不一样的是,Android作为一个移动操作系统,一般情况下,内存资源会比PC更少,所以就需要更加谨慎的管理内存。

Android中的内存管理机制

分配机制
Android为每个进程分配内存的时候,采用了弹性的分配方式,也就是刚开始并不会一下分配很多内存给每个进程,而是给每一个进程分配一个“够用”的量。这个量是根据每一个设备实际的物理内存大小来决定的。随着应用的运行,可能会发现当前的内存可能不够使用了,这时候Android又会为每个进程分配一些额外的内存大小。但是这些额外的大小并不是随意的,也是有限度的,系统不可能为每一个App分配无限大小的内存。

Android系统的宗旨是最大限度的让更多的进程存活在内存中,因为这样的话,下一次用户再启动应用,不需要重新创建进程,只需要恢复已有的进程就可以了,减少了应用的启动时间,提高了用户体验。

回收机制
Android对内存的使用方式是“尽最大限度的使用”,这一点继承了Linux的优点。Android会在内存中保存尽可能多的数据,即使有些进程不再使用了,但是它的数据还被存储在内存中,所以Android现在不推荐显式的“退出”应用。因为这样,当用户下次再启动应用的时候,只需要恢复当前进程就可以了,不需要重新创建进程,这样就可以减少应用的启动时间。只有当Android系统发现内存不够使用,需要回收内存的时候,Android系统就会需要杀死其他进程,来回收足够的内存。但是Android也不是随便杀死一个进程,比如说一个正在与用户交互的进程,这种后果是可怕的。所以Android会有限清理那些已经不再使用的进程,以保证最小的副作用。

Android杀死进程有两个参考条件:

进程优先级:

Android为每一个进程分配了优先级的概念,优先级越低的进程,被杀死的概率就更大。Android中总共有5个进程优先级。具体含义这里不再给出。

前台进程:正常不会被杀死

可见进程:正常不会被杀死

服务进程:正常不会被杀死

后台进程:存放于一个LRU缓存列表中,先杀死处于列表尾部的进程

空进程:正常情况下,为了平衡系统整体性能,Android不保存这些进程

回收收益:

当Android系统开始杀死LRU缓存中的进程时,系统会判断每个进程杀死后带来的回收收益。因为Android总是倾向于杀死一个能回收更多内存的进程,从而可以杀死更少的进程,来获取更多的内存。杀死的进程越少,对用户体验的影响就越小。

官方推荐的App内存使用方式是什么样的?

1、当Service完成任务后,尽量停止它。
因为有Service组件的进程,优先级最低也是服务进程,这会影响到系统的内存回收。IntentService可以很好地完成这个任务。

2、在UI不可见的时候,释放掉一些只有UI使用的资源。
系统会根据onTrimMemory()回调方法的TRIM_MEMORY_UI_HIDDEN等级的事件,来通知App UI已经隐藏了。

3、在系统内存紧张的时候,尽可能多的释放掉一些非重要资源。
系统会根据onTrimMemory()回调方法来通知内存紧张的状态,App应该根据不同的内存紧张等级,来合理的释放资源,以保证系统能够回收更多内存。当系统回收到足够多的内存时,就不用杀死进程了。

4、检查自己最大可用的内存大小。
这对一些缓存框架很有用,因为正常情况下,缓存框架的缓存池大小应当指定为最大内存的百分比,这样才能更好地适配更多的设备。通过getMemoryClass()和getLargeMemoryClass()来获取可用内存大小的信息。

5、避免滥用Bitmap导致的内存浪费。
根据当前设备的分辨率来压缩Bitmap是一个不错的选择,在使用完Bitmap后,记得要使用recycle()来释放掉Bitmap。使用软引用或者弱引用来引用一个Bitmap,使用LRU缓存来对Bitmap进行缓存。

6、使用针对内存优化过的数据容器。
针对移动设备内存有限的问题,Android提供了一套针对内存优化过的数据容器,来替代JDK原生提供的数据容器。但是缺点就是,时间复杂度被提高了。比如SparseArray、SparseBooleanArray、LongSparseArray、

7、意识到内存的过度消耗。
Enum类型占用的内存是常量的两倍多,所以避免使用enum,直接使用常量。

每一个Java的类(包括匿名内部类)都需要500Byte的代码。

每一个类的实例都有12-16 Byte的额外内存消耗。

注意类似于HashMap这种,内部还需要生成Class的数据容器,这会消耗更多内存。

8、抽象代码也会带来更多的内存消耗。
如果你的“抽象”设计实际上并没有带来多大好处,那么就不要使用它。

9、使用nano protobufs 来序列化数据。
Google设计的一个语言和平台中立打的序列化协议,比XML更快、更小、更简单。

10、避免使用依赖注入的框架。
依赖注入的框架需要开启额外的服务,来扫描App中代码的Annotation,所以需要额外的系统资源。

11、使用ZIP对齐的APK。
对APK做Zip对齐,会压缩其内部的资源,运行时会占用更少的内存。

12、使用多进程。

一个符合Android内存管理机制的App应该是什么样的?

一个遵循Android内存管理机制的App。应该具有如下几个特点:

1、更少的占用内存。

2、在合适的时候,合理的释放系统资源。

3、在系统内存紧张的情况下,能释放掉大部分不重要的资源,来为Android系统提供可用的内存。

4、能够很合理的在特殊生命周期中,保存或者还原重要数据,以至于系统能够正确的重新恢复该应用。

在这种管理方式下,如何编写符合Android内存管理机制的App?

主要是参考官方推荐的内存使用方式,来设计和编写App。

避免创建不必要的对象。

在合适的生命周期中,合理的管理资源。

在系统内存不足时,主动释放更多的资源。

编写Android应用时,如何更少的使用内存资源?

避免创建不需要的对象。

比如使用StringBuffer来代替很多个String相加的操作。

使用原始类型来代替包装类型,int比Integer占用更少的资源。

两个并行的属性数组,优于一个包含这两个属性的对象的数组。这个在设计数据容器的时候会有意义,比如类A有两个属性A(int, String),使用 int[] 和 String[] 优于 A[]。

使用常量代替enum。

少用包装类,能够使用原始类型的,就使用原始类型。

开发人员应该注意的App内存管理方式?

内存溢出
内存溢出,就是OOM,也就是内存不够用了。有一个典型的例子就是加载了很多没有经过压缩的Bitmap到内存中,这些Bitmap很大,但是又真的在被使用,必须要在内存中,所以这个时候内存就不够用了。这个时候,App再申请更多内存的时候就不行了,系统会抛出OOM。

解决这种问题:1、减少每个对象占用的内存,比如压缩图片。2、申请大内存。

内存泄露
内存泄露,就是Memory Leak,也就是本来该被GC回收后还给系统的内存,并没有被GC。多数是因为不合理的对象引用,当一个对象不再使用的时候,由于代码问题,没有正确的释放引用,就导致了内存泄露。

解决这种问题:1、通过各种内存分析工具,比如MAT,分析运行时的内存映像文件,找出造成内存泄露的代码,然后修改掉。2、适当的使用WeakReference。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值