内存泄露之Thread

线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控。

1.看一下下面是否存在问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
exampleOne();
}

private void exampleOne() {
new Thread() {
@Override
public void run() {
while (true) {
SystemClock.sleep(1000);
}
}
}.start();
}
}

这段代码很平常也很简单,是我们经常使用的形式。

真的没有问题吗
我们思考一个问题:假设Thread的run函数是一个很费时的操作,当我们开启该线程后,将设备的横屏变为了竖屏,一般情况下当屏幕转换时会重新创建Activity,按照我们的想法,老的Activity应该会被销毁才对,然而事实上并非如此。由于我们的线程是Activity的内部类,所以Thread中保存了Activity的一个引用,当Thread的run函数没有结束时,Thread是不会被销毁的,因此它所引用的老的Activity也不会被销毁,因此就出现了内存泄露的问题。

2.这种线程导致的内存泄露问题应该如何解决呢?

将线程的内部类,改为静态内部类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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对于Activity的引用。

注意上述方法虽然解决了Activity的内存泄露,但是依然存在问题,那就是Activity被销毁后,Thread依然在运行中,占用着资源,对此可以这样解决:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class MainActivity extends Activity {
private MyThread mThread;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
exampleThree();
}

private void 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
protected void onDestroy() {
super.onDestroy();
mThread.close();
}
}

关于Thread的内存泄露,还存在以下的情况,解决办法:

第一、将线程的内部类,改为静态内部类。
第二、在线程内部采用弱引用保存Context引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ThreadActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new MyThread().start();
}

private class MyThread extends Thread {
@Override
public void run() {
super.run();
dosomthing();
}
}
private void dosomthing(){
while(true){
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class ThreadActivity extends AppCompatActivity {

private boolean isRuning = true;

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread);
new MyThread(this).start();
}

private void dosomthing() {
while(isRuning){
System.out.println("yunxing....");
}
}

private void stop(){
isRuning=false;
}

private static class MyThread extends Thread {
WeakReference<ThreadActivity> mThreadActivityRef;

public MyThread(ThreadActivity activity) {
mThreadActivityRef = new WeakReference<>(
activity);
}

@Override
public void run() {
super.run();
if (mThreadActivityRef == null)
return;
if (mThreadActivityRef.get() != null)
mThreadActivityRef.get().dosomthing();
}
}

@Override
protected void onDestroy() {
super.onDestroy();
stop();
}
}

参考链接:
Activitys, Threads, & Memory Leaks
Android App 内存泄露之Thread

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器