关于Android内存优化你应该知道的一切

介绍

Android系统中,内存分配与释放分配在必定程度上会影响App性能的—鉴于其使用的是相似于Java的GC回收机制,所以系统会以消耗必定的效率为代价,进行垃圾回收。
在中国有句老话:”由俭入奢易,由奢返俭难”。而此谚语也彷佛正适应于Android的内存使用。GC回收机制给程序员省去了像C语言程序员那样手动释放内存的工做,可是也带来了一系列的”雷”—动辄内存泄漏,再甚者稍微不慎就会OOM。
这篇文章将会介绍Android的内存管理机制并解释几种在此机制下对内存有影响的几个比较关键的因素。另外,还会介绍如何提升内存管理、检测并避免内存泄漏,以及如何分析内存分配状况android

Here we go!!

Android内存管理机制

Android内存模型并无交换空间(swap space)的概念,而是使用分页(paging)和内存映射(memory-mapping)管理内存,这意味着无论是分配新的对象仍是使用已有的映射页这些内存仍然被占据在RAM里而不能被扇出。所以彻底释放你app内存的惟一方式是释放对象引用以便于能被垃圾回收器回收。
Dalvik虚拟机为每个App分配相应大小的可用内存空间,从2M开始到32M(此最大值根据不一样的厂商通常会有不一样),不能否认,在当前国内各大手机厂商疯狂的拼硬件的时代,这个每一个App的可用内存甚至被提升到了256M,这有效的避免了不少OOM的状况,可是若是程序员所以就无论内存管理任意而为,会为此付出严重代价的(App高卸载率).
Android系统会将在后台运行的App进程保存在一个LRU cache中(不懂的自行百度)。当系统内存紧张时,它会根据LRU的策略kill掉一些优先级比较低的进程。固然,究竟哪个App是当前占用内存最大的程序也是它kill进程时所考虑的一个因素。若是你但愿本身的App在后台运行时能尽量长的”活着”,不被系统kill掉,就要好好的思考如何避免被kill。好比在App转到后台运行以前,尽量的将没有用的内存给释放掉,这样会减小Android系统打印错误日志甚至终止App的可能性。程序员

如何提升Android内存使用

Android系统是世界上使用率最高的手机系统。每一年都有成千上万的年轻人转入到开发Android系统的行列中,可是这些人中,能真正写出稳定、可扩展性强的代码的仍是少数。数据库

如下是提升内存使用的几条建议:
  1. 慎用桥接模式,虽然从程序的设计角度来看,抽象可以帮助咱们建立更加灵活的软件架构。可是在手机系统中,这种设计模式有可能会形成不少反作用。除非大有必要,不然尽可能不要用桥接模式
  2. 避免使用枚举Enum,一个Enum分配的空间是一个普一般量的两倍,所以尽可能少使用枚举
  3. 试着使用Android框架优化后的数据容器,譬如:SparseArray, SparseBooleanArray, 以及 LongSparseArray containers. 使用这些类来替代HashMap的使用。缘由是传统的 HashMap 在内存上的实现十分的低效,由于它须要为 HashMap 中每一项在内存中创建映射关系. 另外, SparseArray类很是高效由于它避免了对key和value的自动封箱. 万事都有两面性,这些个被优化过的容器也不例外,千万记住SparseArray等容器并不适应于内部元素不少的集合,当集合的长度超过1000条时,使用SparseArray进行增删改查的效率远比HashMap低
  4. 避免建立不须要的对象。对于生命周期较短的临时变量,尽可能想办法规避掉每次都要去建立它,这样GC回收被强制调用机会就会更少,留给Android系统进行UI渲染或者音频加载的时间就会更多,从而避免了卡顿现象
  5. 检测App内存中的可用堆的大小,在代码中能够经过动态的调用ActivityManager::getMemoryClass()方法来查询你的App中的可用内存堆大小。若是系统检测到须要分配的内存大小超过了此值,则会抛出OOM错误
  6. **能够适当适应onTrimMemory回调方法。OnTrimMemory 回调是 Android 4.0 以后提供的一个API,这个 API 是提供给开发者的,它的主要做用是提示开发者在系统内存不足的时候,经过处理部分资源来释放内存,从而避免被 Android 系统杀死。这样应用在下一次启动的时候,速度就会比较快。—详情请参阅Android内存优化—OnTrimMemory
  7. 当使用Service应当当心当心再当心!当你须要启动一个服务在后台执行一项任务时,应当在其完成工做以后尽快的中止此服务。能够考虑使用IntentService—当在子线程完成耗时操做以后,IntentService会自动中止并结束自身。然而在实际开发中常常会碰到须要服务去执行一项耗时比较长的任务,好比:音乐播放器,下载APP等等。像这样的应用能够分隔为两个进程:一个进程负责 UI 工做, 另一个则在后台服务中运行其它的工做. 在AndroidManifest 文件中为各个组件申明 android:process 属性就能够分隔为不一样的进程。注意一点:在后台运行的Service绝对不能处理或者持有任何UI,不然系统可能会分配双倍甚至三倍的空间来维护UI资源!!
  8. 当你加载 bitmap 时, 须要根据当前设备的分辨率加载相应分辨率的bitmap进入内存,若是下载下来的原图分辨率比设备分辨率高则要压缩它. 要当心bitmap的分辨率增长后所占用的内存也要进行相应的增长(平方级increase2的增加), 由于它是根据x和y的大小来增长内存占用的
  9. 使用代码混淆工具 ProGuard 经过去除没有用的代码和经过语义模糊来重命名类, 字段和方法来缩小, 优化和混淆你的代码. 使用它能使你的代码更简洁, 更少许的RAM映射页.若是构建apk后你没有作后续的任何处理(包括根据你的证书进行签名), 你必须运行 zipalign 工具为你的apk进行优化, 若是不这样作会致使你的应用使用更多的内存,zipalign以后像资源这样的东西不会再从apk中映射(mmap)入内存.注意:goole play store 不接受没有进行zipalign的apk

针对以上几条,后续会单独再post几篇blog单独讲解。windows

如何避免内存泄漏

程序员在分配内存时若是考虑到了上述9条建议,或许会给App在效率上带来不小的收益,而且能够在后台时依然坚挺(更持久!)。 可是这一切的努力都会由于一个叫作内存泄漏的东东而萎了! 这玩意就如同可乐的存在同样,少喝一点还能扛得住,可是多了的话。。你懂得! 如下是几个常见的形成内存泄漏的状况:设计模式

  • 当查询完数据库以后,及时关闭Cursor对象。
  • 记得在Activity的onPause方法中调用unregisterReceiver()方法,解注册广播
  • 避免Content内存泄漏,好比在4.0.1以前的版本上不要讲Drawer对象置为static。当一个Drawable绑定到了View上,实际上这个View对象就会成为这个Drawable的一个callback成员变量,上面的例子中静态的sBackground持有TextView对象lable的引用,而lable只有Activity的引用,而Activity会持有其余更多对象的引用。sBackground生命周期要长于Activity。当屏幕旋转时,Activity没法被销毁,这样就产生了内存泄露问题。
  • 尽可能不要在Activity中使用非静态内部类,由于非静态内部类会隐式持有外部类实例的引用,当非静态内部类的引用的声明周期长于Activity的声明周期时,会致使Activity没法被GC正常回收掉。
  • 谨慎使用线程Thread!!这条是不少人会犯的错误: Java中的Thread有一个特色就是她们都是直接被GC Root所引用,也就是说Dalvik虚拟机对全部被激活状态的线程都是持有强引用,致使GC永远都没法回收掉这些线程对象,除非线程被手动中止并置为null或者用户直接kill进程操做。因此当使用线程时,必定要考虑在Activity退出时,及时将线程也中止并释放掉
  • 使用Handler时,要么是放在单独的类文件中,要么就是使用静态内部类。由于静态的内部类不会持有外部类的引用,因此不会致使外部类实例的内存泄露–详情请参阅Android中Handler引发的内存泄露

如何分析内存的使用状况

在Mac终端(windows的cmd)中,可使用adb logcat命令来查看或者统计内存的具体使用状况,另外还能够指定包名来查看相应App的内存使用状况。除此以外,还可使用三方的工具来分析Android内存的使用状况,好比:DDMS、MAT(Memory Analyzer tool).api

在adb logcat中,一般能看到GC相关的log以下图所示

Dalvik GC log

GC_Reason 触发GC回收的缘由,可能包含如下几种状况:

  • GC_FOR_ALLOC, 这个是说咱们的应用尝试去分配内存而这时候和heap已经快满了(不够用了),这个时候系统会把咱们的应用停下来而后进行内存回收,一般heap size会增大
  • GC_CONCURRENT,这个应该的当咱们的Heap size 快要被填满的时候触发的一个并发的内存回收
  • GC_EXPLICIT,这个是主动调用系统gc方法触发的GC(在DDMS 点击GC就能够看到)
  • GC_HPROF_DUMP_HEAP 咱们在作内存分析建立HPROF(MAT能够分析该文件)的时候会打印

Amount feed 表示本次垃圾收集释放了多少内存

Heap_stats 当前空闲内存占总内存的百分比

External memory stats 表示API 10及如下的外部分配内存,已分配内存/致使垃圾回收的阈值

Pause_time 应用暂停的时间

一般状况下,生成的GC log越大,表示内存的分配与释放发生的频率越高,这种状况下每每会很是影响用户体验!架构

使用DDMS查看并追踪堆内存的分配状况

经过DDMS,程序员能够很轻松的检测指定进程的内存分配状况。你能够经过“Heap”标签查看最新的实时的堆内存信息,这样能够帮助你辨别出到底是哪个操做最有可能形成大量的内存分配。 “Allocation Tracker” 标签显示的是最近全部的内存分配—包含分配对象的类型,是在哪一个线程中分配等信息。一下图片演示的是使用DDMS展现进程信息—包含了当前进程、对内存分配统计信息。并发

heap_stat

相关文章
相关标签/搜索