性能优化篇---内存管理之Android内存泄露

  • 内存泄漏:当你不再需要某个实例后,但是这个对象却仍然被引用,防止被垃圾回收。这个情况就叫做内存泄露(Memory Leak)。

常见泄漏场景:

1.Handler 导致的内存泄漏

     
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
     
     
public class SampleActivity extends AppCompatActivity {
private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Post a message and delay its execution for 10 minutes.
mLeakyHandler.postDelayed( new Runnable() {
@Override
public void run() { /* ... */ }
}, 1000 * 60 * 1);
// Go back to the previous Activity.
finish();
}
}

原理浅析

当 Android 应用程序启动时,framework 会为该应用程序的主线程创建一个 Looper 对象,负责轮询从Message Queue取数据。

当在主线程中实例化一个 Handler 对象后,会自动与主线程 Looper 的消息队列关联起来。当 Activity 已经结束/销毁,而handmerssage有未处理完的消息时(既message持有Message Queue持有 Handler持有当前 Activity 引用)此时就极有可能导致内存泄漏。



     
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
     
     
private static class MyHandler extends Handler {
private final WeakReference<Sample2Activity> mActivity;
public MyHandler(Sample2Activity activity) {
mActivity = new WeakReference<Sample2Activity>(activity);
}
@Override
public void handleMessage (Message msg) {
Sample2Activity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}
  • 方案1:静态内部类加 WeakRefrence。
  • 方案2:当退出activity时,要注意所在Handler消息队列中的Message是否全部处理完成,可以考虑removeCallbacksAndMessages(null)手动关闭

2.静态变量导致内存泄漏

     
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
     
     
public class Sample3Activity extends AppCompatActivity{
private static Context sContext;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample3);
sContext = this;
//finish();
Button button = (Button)findViewById(R.id.finish);
button.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
}
}

可能导致内存泄漏,原因是:静态变量持有当前 Activity。导致当前 Activity 结束时候,静态变量仍然持有它的引用。可以参见[Android静态变量的生命周期]。建议少引用,或者及时需要手动置空


3.单利模式导致内存泄漏,如果用到Context  ,尽量用ApplicationContext

      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
      
      
public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context.getApplicationContext();
}
public static AppManager getInstance(Context context) {
if (instance != null) {
instance = new AppManager(context);
}
return instance;
}
}

4.非静态内部类持有外部类的实例

     
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
     
     
public class Sample4Activity extends AppCompatActivity {
private static LeakSample mLeakSample = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(mLeakSample == null){
mLeakSample = new LeakSample();
}
//...
}
class LeakSample {
//...
}
}

因为非静态内部类持有外部类对象的引用。正确的做法为: 将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,请使用ApplicationContext。

5.线程造成的内存泄漏

Runnable 是一个匿名内部类( AsyncTask 存在匿名内部类的情况),对当前 Activity 都有一个隐式引用。实例代码如下:

      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
      
      
public class Sample4Activity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample3);
leakSample();
finish();
}
private void leakSample() {
new MyThread().start();
}
private class MyThread extends Thread {
@Override
public void run() {
while ( true) {
SystemClock.sleep( 1000);
}
}
}

当你执行耗时任务,在onDestroy()的时候考虑调用Thread.close(),如果对线程的控制不够强的话,可以使用RxJava自动建立线程池进行控制,并在生命周期结束时取消订阅;

在使用AsyncTask时,在Activity销毁时候也应该取消相应的任务AsyncTask.cancel()方法,避免任务在后台执行浪费资源,进而避免内存泄漏的发生。

6.属性动画导致内存泄漏

属性动画中有一类无线循环的动画,要及时取消,否则最终导致 Activity 无法被释放。动画的特征代码如下:

     
     
1
     
     
animator.setRepeatCount(ValueAnimator.INFINITE);

解决办法自然很简单,在 OnDestory() 中去取消动画即可.


7.资源未关闭造成的内存泄漏

     对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。


其他 :

  • 广播泄露:手动注册广播时,记住退出的时候要unregisterReceiver()
  • 第三方SDK/开源框架泄露:ShareSDK, JPush等第三方SDK需要按照文档控制生命周期
  • 各种callBack/Listener的泄露,要及时设置为Null,特别是static的callback
  • 某些Service也要及时关闭,比如图片上传,当上传成功后,要stopself(),建议用intentservice。
  • Webview需要手动调用WebView.onPause()以及WebView.destory()




     多用泄露检测LeakCanary

    开始使用

    1.在 build.gradle 中加入引用,不同的编译使用不同的引用:

     dependencies {
       debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
       releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
     }
    

    2.在 Application 中:

    public class ExampleApplication extends Application {
    
      @Override public void onCreate() {
        super.onCreate();
        LeakCanary.install(this);
      }
    }
    
    
    

    详见LeakCanary 中文使用说明

    https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/

    这样,就万事俱备了! 在 debug build 中,如果检测到某个 activity 有内存泄露,LeakCanary 就是自动地显示一个通知。

    ,或者MAT仅工具。


    参考:

    http://www.jianshu.com/p/c59c199ca9fa

    http://allenwu.itscoder.com/2016/10/21/allenwu_20161023_oom_in_android_and_solution/





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值