摘要
- 什么是内存泄露
- 内存泄露的危害
- 常见的内存泄露问题
- 实践
什么是内存泄露
内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,
- 这些对象是可达的,即在有向图中,存在通路可以与其相连;
- 这些对象是无用的,即程序以后不会再使用这些对象。
如果对象满足这两个条件,这些对象就可以判定为内存泄漏,这些对象不会被GC所回收,然而它却占用内存。
简单一句话总结就是:不再用到的对象因为被错误引用而无法进行回收。
内存泄露的危害
系统分配给应用程序的内存资源是一定的。对象的创建消耗内存后,因为内存泄露导致这一部分对象不再会被使用到却永远不会被回收,如果该现场高频次发生,会导致:
- app可用内存减少,造成卡顿
- 因为严重内存泄露造成运行时异常OutOfMemeryError,直接反应就是在使用中直接Crash
常见的内存泄露问题
- 单例 ( Context 使用不当造成内存泄露)
- 匿名内部类 ( Handler 使用不当造成的内存泄露)
- 资源使用完未关闭( ContentObserver,File,Cursor,Stream,Bitmap )
- 对象的注册与反注册没有成对出现 ( BroadcastReceiver )
- 线程未及时终止
- 动画没有及时cancel
- 静态引用
实践
对象的注册与反注册没有成对出现
过程分析:BaseFragment#onCreateView 中已经注册 PigeonAutoUnregisterObserver ,当时的本意就是让业务方在页面 onDestroy 中无需反注册,但debug发现该 getLifecycle().addObserver(this); 没有被执行,导致泄露。
解决方法:将 BaseFragment 的生命周期的检测移动到 onCreate 中
匿名内部类+静态引用
过程分析:SpecialFollowTipsDecoration 的构造方法中,调用 FollowManager.addOnPositionChangeListener 添加 new 匿名内部类,而 FollowManager 中存放刚刚添加的回调 sOnPositionChangeListenerList 为静态集合,没有 remove 方法对应。导致 listener没有及时被移除,一直被静态集合持有,无法被回收,又因为匿名内部类会持有外部类,导致 SpecialFollowTipsDecoration 无法被回收,导致泄露
解决方法:
动画没有及时cancel
- 过程分析:CommentBubbleView 中使用 ObjectAnimator,在 ObjectAnimator#start 方法调用时,如下图所示,最终向单例 AnimationHandler 中添加了 callback,但是在页面 onDetachedFromWindow 的回调中却没有及时cancel 动画,导致泄露
- 解决方法:在 onDetachedFromWindow 中 cancel 动画并 remove runnable;cancel 调用流程如下:
Kotlin中的陷阱导致静态引用
以下为一段Kotlin代码
我们通过Tools->Kotlin->Show Kotlin Bytecode工具,将Kotlin代码编译成字节码,再点击Decompile转成Java代码
|
|
- 过程分析:在VideoTimeLimitHelper类加载的静态代码块内,我们创建了一个静态的INSTANCE对象,mDotContext参数也是一个静态成员变量,当setPublishVideoView被调用后,传入的activity被长期持有,导致发生内存泄露。
- 解决方法:将mDotContext参数声明为弱引用,代码如下:
|
|