Android常见内存泄漏

0.

其实很多时候的内存泄漏都是内部类导致的.因为非静态的内部类默认会持有外部类的引用

内存泄漏:某一块内存应该被回收,而由于种种原因无法被回收,也就无法再利用此块内存

内存溢出:(OOM)当程序需要分配一块内存来使用的时候,发现所有的内存都被占用,无法分配新的内存空间导致内存溢出(其实就是内存不够用了,因为在移动设备上每个 app 的内存是非常有限的)

1.Handler 引起的内存泄漏

看代码

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
public class HandlerLeakActivity extends AppCompatActivity {

private final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};

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

handler.postDelayed(new Runnable() {
@Override
public void run() {

}
}, 1000 * 60 * 10);//发送延迟10分钟的消息

finish();
}

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

//handler.removeCallbacksAndMessages(null);
}
}

代码简介:

代码比较简单,就是一个 activity 中有一个 handler, 并且在onCreate()中handler 发送了一个延迟10min 的消息,但是随后, activity 被 finish 了.

内存泄漏原因:

~~因为有一个延迟消息,还没有被处理, 所以 handler 会一直存在,但又由于 handler 是 activity 的内部类.所以 handler又会持有 外部类HandlerLeakActivity 的 activity .导致 activity 无法被回收,从而引起内存泄漏.

解决办法:

~~1.将 handler 改为 static 这样就不会持有 activity

~~2.当 handler 内部需要持有 activity 的时候采用弱引用的方式

~~3.在 activity 被销毁的时候即在 onDestory() 中及时清除掉未被处理的消息

2.非静态内部类的静态对象引起的内存泄漏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class StaticLeakActivity extends AppCompatActivity {

public static InnerClass mInnerClass = null;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(null);

mInnerClass = new InnerClass();
}


private class InnerClass{

}
}

代码简介:

~~有一个内部类 InnerClass, 并且在 onCreate() 中对其进行了初始化操作

内存泄漏原因:

~~同样,因为内部类会持有 activity, 而 InnerClass 的实例却被声明为静态的,因此,InnerClass 的实例生命周期会同 Application 一样.所以,即使 Activity 被 finish 之后,因为 Activity 的一个引用被 static 的 InnerClass 的实例持有,导致无法回销毁,从而引起内存泄漏

解决办法:

~~至于怎么防止内存泄漏的发生,也同样是将 InnerClass 改为 static 让其不持有 Activity 的实例来解决

3.线程引起的内存泄漏

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
45
46
47
48
49
50
51
52
53
public class ThreadLeakActivity extends AppCompatActivity {

private Task task;

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

task = new Task(this);
task.execute();


new Thread(new Runn()).start();
}

@Override
protected void onDestroy() {
super.onDestroy();
task.cancel(true);
}

class Task extends AsyncTask<Void,Void,Void>{

private WeakReference<Context> weakReference;

public Task(Context context){
weakReference = new WeakReference(context);
}

@Override
protected Void doInBackground(Void... voids) {
SystemClock.sleep(1000);
return null;
}

@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
ThreadLeakActivity activity = (ThreadLeakActivity) weakReference.get();
if(activity != null){
//...
}
}
}

class Runn implements Runnable{

@Override
public void run() {
SystemClock.sleep(1000);
}
}
}

代码简介:

~~由于AsyncTask和 Runnable 都可以创建一个线程. so 把它放在一起来看.在 onCreate()中分别开启一个 AsyncTask 的线程和一个 Runable 线程,并且的里面都做了一个耗时的操作

内存泄漏原因:

~~其实也是因为内部类的原因,因为在线程中都有耗时的操作,这时即使 Activity 被 finish, 那么由于两个内部类是非静态的.都会持有 Activity 的引用,导致 Activity 无法被回收,从而引起内存泄漏

解决办法:

其实同 Handler 一样

~~1.将内部类 static 化

~~2.当线程内部需要持有外部引用的时候使用弱引用

~~3.及时的 onDestory() 中cancle 掉未完成的任务

4.单例引起的内存泄漏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SingletonLeak {

private static SingletonLeak singletonLeak = null;
private static Context mContext ;

private SingletonLeak(Context context){//使用applicationContext 来避免内存泄漏
mContext = context.getApplicationContext();
}

public static SingletonLeak getInstance(Context context){
if(singletonLeak == null){
synchronized (SingletonLeak.class){
if(singletonLeak == null){
singletonLeak = new SingletonLeak(context);
}
}
}
return singletonLeak;
}

}

代码简介:

是一个常用的单例模式.但是单例中需要持有一个 context 的引用

内存泄漏原因:

~~如果在创建单例对象的时候传入的 Activity 类型的 context, 那么由于单例的对象是 static 类型的, so 此对象的生命周期同 Application 一样.导致会一直持有此 Activity的引用,从而导致内存泄漏

解决办法:

~~在代码中已经给出了解决办法,就是通过context.getApplicationContext()来将 Activity 类型的 context 转化为 Application 类型的 context, 这样单例的生命周期同 Application 类型的 context 的生命周期都是整个应用程序.就不会存在内存泄漏的问题

5.WebView 导致的内存泄漏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class WebviewLeakActivity extends AppCompatActivity {

private WebView webView;

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

webView.loadUrl("http://www.baidu.com");
}

@Override
protected void onDestroy() {
super.onDestroy();
if(webView != null){
webView.pauseTimers();
webView.removeAllViews();
webView.destroy();
webView = null;
}
//kill 掉进程
//android.os.Process.killProcess(android.os.Process.myPid());
}
}

代码简介:

就是一个 简答webview加载了一个网页

内存泄漏原因:

~~因为 webview 加载网页的时候会持有一个 activity,而实验发现当 webview 加载的网页所在的 Activity 被 finish 后, 虽然及时在 onDestory() 中对 webview 进行了释放处理,但是webview 所占用的内存并没有被回收.当此网页的图片等资源比较多时,占用的内存也会同步增大.又由于所占用的内存无法及时被回收,从而导致内存泄漏,可以想象当多次的开关这样的 Activity 的时候,内存泄漏会很多.非常有导致可能 oom 的发生

解决办法:

~~当网页所占用的内存比较大的情况下,考虑将这个 Activity 放入到一个新的进程中来执行,当不在需要的时候及时的来杀死这个进程来释放掉所占用的内存,代码中已有体现