内存泄露从入门到精通三部曲之常见缘由与用户实践
腾讯Bugly特约做者: 姚潮生java
1.集合类数组
集合类若是仅仅有添加元素的方法,而没有相应的删除机制,致使内存被占用。若是这个集合类是全局性的变量 (好比类中的静态属性,全局性的 map 等即有静态引用或 final 一直指向它),那么没有相应的删除机制,极可能致使集合所占用的内存只增不减。缓存
2.单例模式markdown
不正确使用单例模式是引发内存泄露的一个常见问题,单例对象在被初始化后将在 JVM 的整个生命周期中存在(以静态变量的方式),若是单例对象持有外部对象的引用,那么这个外部对象将不能被 JVM 正常回收,致使内存泄露网络
3.Android组件或特殊集合对象的使用app
BraodcastReceiver,ContentObserver,FileObserver,Cursor,Callback等在 Activity onDestroy 或者某类生命周期结束以后必定要 unregister 或者 close 掉,不然这个 Activity 类会被 system 强引用,不会被内存回收。异步
不要直接对 Activity 进行直接引用做为成员变量,若是不得不这么作,请用 private WeakReference mActivity 来作,相同的,对于Service 等其余有本身声明周期的对象来讲,直接引用都须要谨慎考虑是否会存在内存泄露的可能。函数
4. Handleroop
要知道,只要 Handler 发送的 Message 还没有被处理,则该 Message 及发送它的 Handler 对象将被线程 MessageQueue 一直持有。因为 Handler 属于 TLS(Thread Local Storage) 变量, 生命周期和 Activity 是不一致的。所以这种实现方式通常很难保证跟 View 或者 Activity 的生命周期保持一致,故很容易致使没法正确释放。如上所述,Handler 的使用要尤其当心,不然将很容易致使内存泄露的发生。测试
5.Thread 内存泄露
线程也是形成内存泄露的一个重要的源头。线程产生内存泄露的主要缘由在于线程生命周期的不可控。好比线程是 Activity 的内部类,则线程对象中保存了 Activity 的一个引用,当线程的 run 函数耗时较长没有结束时,线程对象是不会被销毁的,所以它所引用的老的 Activity 也不会被销毁,所以就出现了内存泄露的问题。
6.一些不良代码形成的内存压力
有些代码并不形成内存泄露,可是它们,或是对没使用的内存没进行有效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新内存。
6.1 Bitmap 没调用 recycle().
Bitmap 对象在不使用时,咱们应该先调用 recycle() 释放内存,而后才它设置为 null. 由于加载 Bitmap 对象的内存空间,一部分是 java 的,一部分 C 的(由于 Bitmap 分配的底层是经过 JNI 调用的 )。 而这个 recyle() 就是针对 C 部分的内存释放。
6.2 构造 Adapter 时,没有使用缓存的 convertView。
1. callback只有add操做,没有注销remove
从引用关系能够看到当前 view 被 callback 引用,而 callback 被外部对象 sharkprotocolQueue 持有引用而致使泄漏。
2. 发送延时消息时,若是该消息未处理,在退出页面后会致使该页面没法回收。
Android 应用启动的时候会建立 UI 主线程的 Looper 对象,它存在于整个应用的生命周期,用于处理消息队列里的 Message。而这些 Message 会引用发送该消息的 Handler 对象。
那么问题来了,若是这些 Handler 是 Activity 的内部类,那么当这些 Handler 的消息未处理完或者消息自己是延时消息的话,就会致使 Activity 退出后,从 Activity 到 Handler 到 Message 到 Looper 的引用链条一直存在,从而致使 Activity 的泄露!
3. 异步线程未完成前退出 Activity 等组件,可能会致使界面资源没法释放。
这种状况是典型的线程对象致使的内存泄露。缘由也很简单,线程 Thread 对象的 run 任务未执行完以前,对象自己是不会释放的。所以 Activity 等组件对象内的线程对象成员若是有耗时任务(通常也都是耗时任务),就会致使一直持有组件自己的引用内存泄露!
本文部份内容和经验摘自网络,结合本次内存泄露的排查总结予以概括。
对activity等组件的引用应该控制在activity的生命周期以内; 若是不能就考虑使用
getApplicationContext或者getApplication,以免activity被外部长生命周期的对象引用而泄露
在代码复审的时候关注长生命周期对象:全局性的集合、单例模式的使用、类的static变量等等。
尽可能不要在静态变量或者静态内部类中使用非静态外部成员变量(包括context),即便要使用,也要考虑适时把外部成员变量置空;也能够在内部类中使用弱引用来引用外部类的变量;
Handler的持有的引用对象最好使用弱引用,资源释放时也能够清空Handler里面的消息。好比在Activity onStop或者onDestroy的时候,取消掉该Handler对象的Message和Runnable:
removeCallbacks(Runnable r)或removeMessages(int what),或removeCallbacksAndMessages(null)等。
线程Runnable执行耗时操做,注意在页面返回时及时取消或者把Runnable写成静态类。
a) 若是线程类是内部类,改成静态内部类。
b) 线程内若是须要引用外部类对象如context,须要使用弱引用。
在Java的实现过程当中,也要考虑其对象释放,最好的方法是在不使用某对象时,显式地将此对象赋空,如清空对图片等资源有直接引用或者间接引用的数组(使用array.clear();array = null),最好遵循谁建立谁释放的原则。
Bugly是腾讯内部产品质量监控平台的外发版本,其主要功能是App发布之后,对用户侧发生的Crash以及卡顿现象进行监控并上报,让开发同窗能够第一时间了解到App的质量状况,及时机型修改。目前腾讯内部全部的产品,均在使用其进行线上产品的崩溃监控。