上一节已经介绍了内存泄漏与引用的关系,那么这一节强化一下,看看匿名类跟内存泄漏和引用又存在着什么样的关系呢。
匿名类定义
在实际的项目中看到一个很奇怪的现象,Java可以直接new一个接口,然后在new里面粗暴的加入实现代码,向下面这样:
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(this.getClass());
}
};
runnable.run();
匿名类相当于在定义类的同时再新建这个类的实例
知识点
匿名类也持有着外部类的强引用。
案例1_匿名AsyncTsk
当你在Activity中定义了匿名的AsyncTsk,当异步任务在后台执行耗时任务期间,Activity不幸被销毁了(用户退出,系统回收),这个被AsyncTask持有的Activity实例就不会被垃圾回收器回收,直到异步任务结束。
我们来看如下代码:
//创建匿名类
void startAsyncTask() {
new AsyncTask<Void, Void, Void>() {
@Override protected Void doInBackground(Void... params) {
while(true);
}
}.execute();
}
//匿名类使用
button.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
startAsyncTask();
}
});
如上代码中匿名类AsyncTask执行期间,所有持有的引用Activity被销毁,那么这个activity实例就无法被垃圾回收器回收,直到异步任务结束。
案例2_匿名Handler
同样道理,定义匿名的Runnable,用匿名类Handler执行。Runnable内部类会持有外部类的隐式强引用,被传递到Handler的消息队列MessageQueue中,在Message消息没有被处理之前,Activity实例不会被销毁了,于是导致内存泄漏。
//创建匿名类
void createHandler() {
new Handler() {
@Override public void handleMessage(Message message) {
super.handleMessage(message);
}
}.postDelayed(new Runnable() {
@Override public void run() {
while(true);
}
}, Long.MAX_VALUE >> 1);
}
//匿名类使用
button.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
createHandler();
}
});
案例3_匿名Thread
定义匿名的Thread未结束之前,Activity实例不会被销毁,也会导致内存泄漏。
//创建匿名类
void spawnThread() {
new Thread() {
@Override public void run() {
while(true);
}
}.start();
}
//匿名类使用
button.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
spawnThread();
}
});
案例4_匿名TimerTask
定义匿名的TimerTask未结束之前,Activity实例不会被销毁,也会导致内存泄漏。
//创建匿名类
void scheduleTimer() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
while(true);
}
}, Long.MAX_VALUE >> 1);
}
//匿名类使用
button.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
scheduleTimer();
}
});
总结
看了上面4个例子,我们来总结一下:只要是匿名类的实例,不管是不是在工作线程中,都会持有外部类(Activity)的强引用,导致内存泄漏。
上一节:Android内存泄漏(一):引用
下一节:Android内存泄漏(三):单例
网友评论