内存泄漏: 对象在内存heap堆中中分配的空间, 当再也不使用或没有引用指向的状况下, 仍不能被GC正常回收的状况。 多数出如今不合理的编码状况下, 好比在Activity中注册了一个广播接收器, 可是在页面关闭的时候进行unRegister, 就会出现内存溢出的现象。 一般状况下, 大量的内存泄漏会形成OOM。java
OOM: 即OutOfMemoery, 顾名思义就是指内存溢出了。 内存溢出是指APP向系统申请超过最大阀值的内存请求, 系统不会再分配多余的空间, 就会形成OOM error。 在咱们Android平台下, 多数状况是出如今图片不当处理加载的时候。内存管理之道嘛, 无非就是先理解并找出内存泄漏的缘由, 再基于这些反式去合理的编码, 去防范进而避免内存开销过大的情形。 学习如何合理的管理内存, 最好先了解内存分配的机制和原理。 只有深层次的理解了内部的原理, 才能真正避免OOM的发生。android
Android APP的所能申请的最大内存大小是多少, 有人说是16MB, 有人又说是24MB。 这种事情, 仍是亲自用本身的手机测试下比较靠谱。 测试方式也比较简单, Java中有个Runtime类, 主要用做APP与运行环境交互, APP并不会为咱们建立Runtime的实例, 可是Java为咱们提供了单例获取的方式Runtime.getRuntime()。 经过maxMemory()方法获取系统可为APP分配的最大内存, totalMemory()获取APP当前所分配的内存heap空间大小。 我手上有两部手机, 一部Oppo find7, 运行Color OS, 实测最大内存分配为192MB; 一部天语v9, 运行小米系统, 实测最大内存分配为100MB。 这下看出点眉目了吧, 因为Android是开源系统, 不一样的手机厂商实际上是拥有修改这部分权限能力的, 因此就形成了不一样品牌和不一样系统的手机, 对于APP的内存支持也是不同的, 和IOS的恒久100MB是不一样的。 通常来讲, 手机内存的配置越高, 厂商也会调大手机支持的内存最大阀值, 尤为是如今旗舰机满天发布的状况下。 可是开发者为了考虑开发出的APP的内存兼容性, 没法保证APP运行在何种手机上, 只能从编码角度来优化内存了。算法
Android内存优化的关键点。shell
1、 万恶的static数据库
static是个好东西, 声明赋值调用就是那么的简单方便, 可是伴随而来的还有性能问题。 因为static声明变量的生命周期实际上是和APP的生命周期同样的, 有点相似与Application。 若是大量的使用的话, 就会占据内存空间不释放, 聚沙成塔也会形成内存的不断开销, 直至挂掉。 static的合理使用通常用来修饰基本数据类型或者轻量级对象, 尽可能避免修复集合或者大对象, 经常使用做修饰全局配置项、 工具类方法、 内部类。缓存
2、 无关引用网络
不少状况下, 咱们需求用到传递引用, 可是咱们没法确保引用传递出去后可否及时的回收。 好比比较有表明性的Context泄漏, 不少状况下当Activity结束掉后, 因为仍被其余的对象指向致使一直迟迟不能回收, 这就形成了内存泄漏。 这时能够考虑第三条建议。框架
3、 善用SoftReference/WeakReference/LruCache异步
Java、 Android中有没有这样一种机制呢, 当内存吃紧或者GC扫过的状况下, 就能及时把一些内存占用给释放掉, 从而分配给须要分配的地方。 答案是确定的, java为咱们提供了两个解决方案。 若是对内存的开销比较关注的APP, 能够考虑使用WeakReference, 当GC回收扫过这块内存区域时就会回收; 若是不是那么关注的话, 可使用SoftReference, 它会在内存申请不足的状况下自动释放, 一样也能解决OOM问题。 同时Android自3.0之后也推出了LruCache类, 使用LRU算法就释放内存, 同样的能解决OOM, 若是兼容3.0一下的版本, 请导入v4包。 关于第二条的无关引用的问题, 咱们传参能够考虑使用WeakReference包装一下。工具
4、 谨慎handler
在处理异步操做的时候, handler + thread是个不错的选择。 可是相信在使用handler的时候, 你们都会遇到警告的情形, 这个就是lint为开发者的提
醒。 handler运行于UI线程, 不断处理来自MessageQueue的消息, 若是handler还有消息须要处理可是Activity页面已经结束的状况下, Activity的引用其实并不会被回收, 这就形成了内存泄漏。 解决方案, 一是在Activity的onDestroy方法中调用
handler.removeCallbacksAndMessages(null);取消全部的消息的处理, 包括待处理的消息; 二是声明handler的内部类为static。
5、 Bitmap终极杀手
Bitmap的不当处理很可能形成OOM, 绝大多数状况都是因这个缘由出现的。 Bitamp位图是Android中当之无愧的胖小子, 因此在操做的时候固然是十分的当心了。 因为Dalivk并不会主动的去回收, 须要开发者在Bitmap不被使用的时候recycle掉。 使用的过程当中, 及时释放是很是重要的。 同时若是需求容许, 也能够去BItmap进行必定的缩放, 经过BitmapFactory.Options的inSampleSize属性进行控制。 若是仅仅只想得到Bitmap的属性, 其实并不须要根据BItmap的像素去分配内存, 只需在解析读取Bmp的时候使用BitmapFactory.Options的inJustDecodeBounds属性。 最后建议你们在加载网络图片的时候, 使用软引用或者弱引用并进行本地缓存, 推荐使用android-universal-imageloader或者xUtils, 牛人出品, 必属精品。 前几天在讲《 自定义控件( 三) 继承控件》 的时候, 也整理一个, 你们能够去Github下载看看。
6、 Cursor及时关闭
在查询SQLite数据库时, 会返回一个Cursor, 当查询完毕后, 及时关闭, 这样就能够把查询的结果集及时给回收掉。
7、 页面背景和图片加载
在布局和代码中设置背景和图片的时候, 若是是纯色, 尽可能使用color; 若是是规则图形, 尽可能使用shape画图; 若是稍微复杂点, 可使用9patch图; 若是不能使用9patch的状况下, 针对几种主流分辨率的机型进行切图。
8、 ListView和GridView的item缓存
对于移动设备, 尤为硬件良莠不齐的android生态, 页面的绘制实际上是很耗时的, findViewById也是蛮慢的。 因此不重用View, 在有列表的时候就尤其显著了, 常常会出现滑动很卡的现象。 具体参照历史文章《 说说ViewHolder的另外一种写法》
9、 BroadCastReceiver、 Service
绑定广播和服务, 必定要记得在不须要的时候给解绑。
10、 I/O流
I/O流操做完毕, 读写结束, 记得关闭。
11、 线程
线程再也不须要继续执行的时候要记得及时关闭, 开启线程数量不易过多, 通常和本身机器内核数同样最好, 推荐开启线程的时候, 使用线程池。
12、 String/StringBuffer
当有较多的字符创须要拼接的时候, 推荐使用StringBuffer。
内存溢出
在安卓的应用程序中,内存溢出主要提要在几个方面
1.ListView的显示
咱们在使用ListView的时候,都会给他设置Adapter,若是在Adapter中的getView方法 中,咱们没有复用convertView,就会形成在滑动ListView的时候,会为每个item都 生成一个View对象,而无论这个item以前是否已经生成过View对象。若是来回滑动 的次数太多的话,就会形成View生成的数量太多,最终会形成内存溢出。
若是ListView中的item是包含图片的,那么,若是在快速滑动的过程当中,咱们就去为item加载图片,此时很是容易形成内存溢出。由于,在快速滑动的过程当中,垃圾回收器还来不及回收内存,而新的item又须要新的内存来显示图片。因此,在这种状况下,通常的作法是监听ListView的滚动状态,当ListView的滚动状态为空闲的状况下,里面 的每个Item才去加载图片。
2.加载图片相关
a、加载多张图片
对于加载多张图片,咱们通常会使用三级缓存来实现图片的加载。内存缓存、本地缓存、网络缓存。缓存的目的是为了下一次加载速度更快。因此在内存中保存着必定数量的图片是有助于下一次图片显示的速度。可是,内存中不能保存太多的图片对象,因此咱们使用LRUCache来保存内存中的图片,而且控制在必定的数量以内。
b、加载单张图片
这种状况是针对于某一张图片特别大的状况。若是一张图片很是很是大,若是50M,那么我只要一去加载它,那么个人程序确定就会挂,根本还没使用到三级缓存应用程序就受不了了。因此,对于大图片的显示须要特殊处理。由于图片虽然特别大,可是这个图片所须要显示的控件有多是很小的。咱们能够先把图片的宽和高获得,再获得这张图片所须要显示的控件的宽高,就能够获得图片和控件的缩放比例了。最后,根据缩放比例,设置图片的采样率,来减少单张图片的内存占用。
解决方案
不管是内存泄露仍是内存溢出,最终的后果基本上是一致的,那就是形成应用程序强行关闭。在应用程序的功能开发完以后,怎么样才能肯定应用程序有没有内存的问题呢?又怎样来肯定究竟是哪一块代码出的问题呢?接下来咱们就来讲说关于内存问题的解决方案。
1、使用monkey工具
由于有些内存问题藏的比较深,要长期使用才能出现异常。因此可使用monKey工具 来对咱们的应用程序进行压力测试。
模拟200000次用户操做的参考命令: adb shell monkey -s 23 -p cn.itcast.XXX(所测试的 包) --ignore-crashes --ignore-timeouts -v -v -v 200000 > D:\文件名.log
回车以后,咱们所须要测试的应用程序就启动了,而且还有不断触摸事件被促发,程序 生成的log会保存在D目录下。
2、捕获OOM异常
自定义Application并让它实现UncaughtExceptionHandler 接口,在onCreate方法中让本身成为系统的默认异常处理机制。Thread.setDefaultUncaughtExceptionHandler(this);
这样子设置以后,若是应用程序出现异常,就会调用Application中的uncaughtException 方法。咱们就在这个方法中判断异常是否为OOM异常。若是是OOM异常,就将内存快照导到sd卡中去。
3.用第三方开源框架分析
leakcanary 是一个开源项目,一个内存泄露自动检测工具,是著名的 GitHub 开源组织 Square 贡献的,它的主要优点就在于自动化过早的发觉内存泄露、配置简单、抓取贴心,缺点在于还存在一些bug,不过正常使用百分之九十状况是OK的,其核心原理与MAT工具相似。