问:简单解释下 Thread 是如何造成内存泄漏的?
答:解释这个问题需要举几个例子。
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread() {
@Override
public void run() {
while(true) {
SystemClock.sleep(10);
}
}
}.start();
}
}
上面例子中的匿名内部类 Thread 实例会长久运行,不会被系统 GC 回收。Threads 在 Java 中是 GC roots,意味着 Dalvik Virtual Machine (DVM) 会为所有活跃的 threads 在运行时系统中保持一个硬引用,这会导致 threads 一直处于运行状态,垃圾收集器将永远不可能回收它。
其次,非静态内部类会持有外部类的引用。Thread 会长久地持有 Activity 的引用,使得系统无法回收 Activity 和它所关联的资源和视图。
针对后者,MainActivity 的内存泄漏问题,只需把非静态的 Thread 匿名类定义成静态的内部类就行了。
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
exampleTwo();
}
private void exampleTwo() {
new MyThread().start();
}
private static class MyThread extends Thread {
@Override
public void run() {
while(true) {
SystemClock.sleep(1000);
}
}
}
}
现在新创建的 Thread 不会持有 MainActivity 的一个隐式引用,也就不会阻止垃圾回收器对 MainActivity 的回收工作了。
而在第一个问题中,一旦有一个新的 Activity 创建,那么就有一个 Thread 永远得不到清理回收,发生内存泄漏。
出于这个原因,我们应当为 thread 添加一个结束的逻辑,如下代码所示:
public class MainActivity extends Activity {
private MyThread mThread;
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
exampleThree();
}
privatevoid exampleThree() {
mThread = new MyThread();
mThread.start();
}
/**
* Static inner classes don't hold implicit references to their
* enclosing class, so the Activity instance won't be leaked across
* configuration changes.
*/
private static class MyThread extends Thread {
private boolean mRunning = false;
@Override
public void run() {
mRunning = true;
while(mRunning) {
SystemClock.sleep(1000);
}
}
public void close() {
mRunning = false;
}
}
@Override
protectedvoid onDestroy() {
super.onDestroy();
mThread.close();
}
}
在上述的代码中,当 Activity 结束销毁时在 onDestroy()
方法中结束了新创建的线程,保证了 thread 不会发生泄漏。但是如果你想在手机配置发生改变时,保持原有的线程而不重新创建的话,你可以考虑使用 fragment
来保留原有的线程,以备下一次使用。
因此得出如下结论:
使用静态内部类/匿名类,不要使用非静态内部类/匿名类。非静态内部类/匿名类会隐式地持有外部类的引用,外部类就有可能发生泄漏。而静态内部类/匿名类不会隐式地持有外部类引用,外部类会以正常的方式回收,如果你想在静态内部类/匿名类中使用外部类的属性或方法时,可以显式地持有一个弱引用。
不要以为 Java 永远会帮你清理回收正在运行的 threads。在上面的代码中,我们很容易误以为当 Activity 结束销毁时会帮我们把正在运行的 thread 也结束回收掉,但事情永远不是这样的!Java threads 会一直存在,只有当线程运行完成或被杀死掉,线程才会被回收。所以我们应该养成为 thread 设置退出逻辑条件的习惯。
作者:方衍
链接:https://www.jianshu.com/p/f50366145b4b
來源:简书
本文引用自 Thread 是怎么造成内存泄露的?