【Android】常见的内存泄漏场景分析

内存泄漏场景

  • 资源性对象未关闭。比如Cursor、File文件等,往往都用了一些缓冲,在不使用时,应该及时关闭它们。
  • 注册对象未注销。比如事件注册后未注销,会导致观察者列表中维持着对象的引用。
  • 类的静态变量持有大数据对象。
  • 非静态内部类的静态实例。
  • Handler临时性内存泄漏。如果Handler是非静态的,容易导致Activity或Service不会被回收。
  • 容器中的对象没清理造成的内存泄漏。
  • WebView。WebView存在着内存泄漏的问题,在应用中只要使用一次WebView,内存就不会被释放掉。

内存泄漏工具

推荐该博主写的利用 LeakCanary 来检查 Android 内存泄漏

具体场景:

因为静态变量造成的内存泄漏

单例模式应该是每个Android 开发者最熟悉的设计模式了,但是如果使用不当,在Android 应用中很可能会造成内存泄漏。举个例子

public class MyApplication {
 
	private static MyApplication application;
	private MyApplication(Context context){
	}
	public static MyApplication getApplication(Context context){
		if(application==null){
			application = new MyApplication(context);
		}
		return application;
	}
}

我创建了一个MyApplication 类,在构造方法中,我传入一个Context。Context 包括Application\Activity\Service,如果我传入的是Application,那么不会有什么影响,因为我所需要的时间是一整个程序的生命周期。

但是我如果传入的是Activity,那么,当我退出该Activity时,因为被static 修饰,单例对象持有对该Activity的引用,导致该Activity不会被回收,从而产生内存泄漏。

解决方案:

在获取单例的方法中,把原有的context 改成application = new MyApplication(context.getApplicationContext());

把传入的Context的生命周期设置成与Application 一样长,这样在使用单例的时候就和导入的Context 类型无关,防止了内存泄漏。

Handler 的错误使用

Handler是开发中异步处理最常使用到的工具之一,但是如果错误地使用Handler 也极容易产生内存泄漏。

举个栗子:

public class Act_test extends Activity{
	private static final int WHAT = 1;
	private Handler mHandler = new Handler(){
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case WHAT:
				//handle it
				break;
			default:
				break;
			}
		};
	};
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		Message message = Message.obtain();
		message.what = WHAT;
		mHandler.sendMessage(message);
		
	}
}

怎么样,看起来是不是合情合理,但是其中隐藏着一个重要的隐患,当我们创建出一个Handler 的时候,代码中的mHandler为Handler 的非静态内部类的实例,所以mHandler 持有对外部类,即Activity 的引用。并且Handler 中的Looper不断轮询消息队列中的message,message 又持有mHandler的引用,但mHandler这玩意儿又持有Activity 的引用,所以这下倒好,因为你一个message 导致我整个Activity都无法被回收,你说气人不气人。

解决方案:

public class Act_test extends Activity{
	private static final int WHAT = 1;
	private MyHandler mHandler = new MyHandler(this);
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		Message message = Message.obtain();
		message.what = WHAT;
		mHandler.sendMessage(message);
		
	}
	static class MyHandler extends Handler{
		WeakReference<Context> reference;
		public MyHandler(Context context) {
			// TODO Auto-generated constructor stub
			reference = new WeakReference<Context>(context);
		}
		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			if(reference!=null){
				if(msg.what==WHAT){
				//handle it
				}
			}
		}
	}
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		mHandler.removeCallbacksAndMessages(null);
	}
}

解释:首先,创建一个Handler 的静态内部类,这样,它将不再持有外部类的引用,并且将持有它的Context 进行弱引用,确保Activity 可以及时被回收。在前面分析过,Handler 内存泄漏是因为消息队列中还有未处理的Message ,所以当该Activity 被销毁时,将消息队列中的Message 清空即可。

非静态内部类的静态实例的错误使用

该错误与上例原因接近,先看错误代码:

public class Act_test extends Activity{
 
	private static Test mTest;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		if(mTest==null){
			mTest = new Test();
		}
	}
	class Test{
		
	}
}

这种情况多使用于反复使用同一个Activity 为了避免重复创建资源。

但是这种写法同样存在着隐患,因为在该Activity中,我创建了一个Test 类的静态实例,每次启动Activity 都会使用该单例。但是由于非静态内部类持有外部类的使用,并且该非静态内部类又创建了一个静态的实例,这导致了该单例的存活时间与application的生命周期一样长,于是会一直持有该Activity的引用,导致其无法被回收。

解决方案:static class Test{

}

将Test 设置为静态内部类。

不正确使用线程,造成内存泄漏

由于创建线程时用到了Runnable 为匿名内部类,持有对外部类的引用,但是如果该外部类在销毁之前,线程中还有未完成的任务,这将导致该外部类无法被回收。

解决方案:对其进行弱引用

资源没有及时关闭

在开发中,例如Cursor、File、IOStream等资源在使用后要及时进行关闭,避免造成内存的浪费。

TypedArray、Bitmap等要及时进行recycle。

EventBus 、BroadcastReceiver注册后避免重复注册,使用后及时销毁。

文章来源:https://blog.csdn.net/adrian24/article/details/53248255

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值