一篇文章教你搞定内存泄漏与排查流程——安卓性能优化( 四 )


当我们解决完后,可以再次进行一轮内存快照,直到没有内存泄漏,过程会比较枯燥,但一点点的解决泄漏最终会给app一个质的飞跃 。
7、常见的内存泄漏原因(1)集合类
集合类如果仅仅有添加元素的方法,而没有相应的删除机制,导致内存被占用 。如果这个集合类是全局性的变量 (比如类中的静态属性,全局性的 map 等即有静态引用或 final 一直指向它),那么没有相应的删除机制,很可能导致集合所占用的内存只增不减 。
(2)单例模式
不正确使用单例模式是引起内存泄露的一个常见问题,单例对象在被初始化后将在 JVM 的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部对象的引用,那么这个外部对象将不能被 JVM 正常回收,导致内存泄露 。
public class SingleTest{ private static SingleTest instance; private Context context; private SingleTest(Context context){ this.context = context; } public static SingleTest getInstance(Context context){ if(instance != null){ instance = new SingleTest(context); } return instance; }}这里如果传递Activity作为Context来获得单例对象,那么单例持有Activity的引用,导致Activity不能被释放 。 不要直接对 Activity 进行直接引用作为成员变量,如果允许可以使用Application 。 如果不得不需要Activity作为Context,可以使用弱引用WeakReference,相同的,对于Service 等其他有自己生命周期的对象来说,直接引用都需要谨慎考虑是否会存在内存泄露的可能 。
(3)未关闭或释放资源
BroadcastReceiver,ContentObserver,FileObserver,Cursor,Callback等在 Activity onDestroy 或者某类生命周期结束之后一定要 unregister 或者 close 掉,否则这个 Activity 类会被 system 强引用,不会被内存回收 。值得注意的是,关闭的语句必须在finally中进行关闭,否则有可能因为异常未关闭资源,致使activity泄漏
(4)Handler
只要 Handler 发送的 Message 尚未被处理,则该 Message 及发送它的 Handler 对象将被线程 MessageQueue 一直持有 。特别是handler执行延迟任务 。所以,Handler 的使用要尤为小心,否则将很容易导致内存泄露的发生 。
public class MainActivity extends AppCompatActivity { private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { //do something } }; private void loadData(){ //do request Message message = Message.obtain(); mHandler.sendMessage(message); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); loadData(); }}这种创建Handler的方式会造成内存泄漏,由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏,所以另外一种做法为:
public class MainActivity extends AppCompatActivity { private MyHandler mHandler = new MyHandler(this); private void loadData() { //do request Message message = Message.obtain(); mHandler.sendMessage(message); } private static class MyHandler extends Handler { private WeakReference<Context> reference; public MyHandler(Context context) { reference = new WeakReference<Context>(context); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); MainActivity mainActivity = (MainActivity) reference.get(); if (mainActivity != null) { //do something to update UI via mainActivity } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); loadData(); }}创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在回收时也可以回收Handler持有的对象,这样虽然避免了Activity泄漏,不过Looper线程的消息队列中还是可能会有待处理的消息,所以我们在Activity的Destroy时或者Stop时应该移除消息队列中的消息,
@Overrideprotected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null);}使用mHandler.removeCallbacksAndMessages(null);是移除消息队列中所有消息和所有的Runnable 。当然也可以使用mHandler.removeCallbacks();或mHandler.removeMessages();来移除指定的Runnable和Message 。
(5)Thread
和handler一样,线程也是造成内存泄露的一个重要的源头 。线程产生内存泄露的主要原因在于线程生命周期的不可控 。比如线程是 Activity 的内部类,则线程对象中保存了 Activity 的一个引用,当线程的 run 函数耗时较长没有结束时,线程对象是不会被销毁的,因此它所引用的老的 Activity 也不会被销毁,因此就出现了内存泄露的问题 。


推荐阅读