java thread 内存泄露_Java Thread 是怎么造成内存泄露的?

问:简单解释下 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 设置退出逻辑条件的习惯。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值