Android防止内存泄漏以及MAT的使用

Android发生内存泄漏最普遍的一种情况就是长期保持对Context,特别是Activity的引用,使得Activity无法被销毁。这也就意味着Activity中所有的成员变量也没办法销毁。本文仅介绍如何避免这种情况的发生,其他如Bitmap没有及时回收导致的OOM异常暂不讨论。

一、防止内存泄漏

什么情况下会长时间保持对某个Activity的引用呢?主要有以下两种情况:

1、某个static变量保持对Activity的引用

2、线程保持Activity的引用

由于静态变量是长驻内存的,甚至在你调用了exit方法后仍不会被销毁。而线程的生命周期是无法预料的。一旦发生了切屏,Activity就无法被销毁。下面来看一个简单的例子:

[java] view plaincopy

  1. public class MainActivity extends Activity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_main);
  6. }
  7. public void onClick(View v){
  8. new MyThread().start();
  9. }
  10. class MyThread extends Thread{
  11. public void run(){
  12. try {
  13. Thread.sleep(60000);
  14. } catch (InterruptedException e) {
  15. // TODO Auto-generated catch block
  16. e.printStackTrace();
  17. }
  18. }
  19. }
  20. }

线程MyThread是Activity的内部类,这有什么问题呢?我们知道,内部类是会持有外部类的引用的,这就是上面说到的第二种情况。那么怎么优化,使线程不持有Activity的引用呢?熟悉Java的马上就会想到静态内部类,没错,只要在定义MyThread的时候加一个static,这样这个内部类就不会持有外部类的引用了。

然而在实际应用中,线程的问题往往要比这复杂很多。我们可能还要用到AsyncTask或者Handler加Thread来异步获取数据并刷新界面。此时如果只是简单的将AsyncTask或者Handler定义成静态的,那么无法实现刷新界面的功能。之前的做法是使用弱引用(WeakReference),但是2.3以后的Android加强了对弱引用及软引用的回收,如果在内存不是很足的情况下,很可能导致你的引用过早被系统回收,而无法做刷新界面的操作。接下来的这个例子将提供一种新的解决方案:

[java] view plaincopy

  1. public class MainActivity extends Activity {
  2. private MyThread t;
  3. private String TAG = "tag";
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.activity_main);
  8. }
  9. @Override
  10. protected void onDestroy() {
  11. if(t != null){
  12. t.detach();
  13. }
  14. super.onDestroy();
  15. }
  16. public void onClick(View v){
  17. if(t != null){
  18. t.detach();
  19. }
  20. t = new MyThread();
  21. Callback callback = new Callback(){
  22. @Override
  23. public void finish() {
  24. Log.v(TAG, "finish");
  25. }
  26. };
  27. t.attach(callback);
  28. t.start();
  29. }
  30. static interface Callback{
  31. void finish();
  32. }
  33. static class MyThread extends Thread {
  34. Callback callback;
  35. void attach(Callback callback){
  36. this.callback = callback;
  37. }
  38. void detach(){
  39. if(callback != null){
  40. callback = null;
  41. }
  42. }
  43. public void run(){
  44. try {
  45. Thread.sleep(60000);
  46. } catch (InterruptedException e) {
  47. // TODO Auto-generated catch block
  48. e.printStackTrace();
  49. }
  50. if(callback != null){
  51. callback.finish();
  52. }
  53. }
  54. }
  55. }

第二个例子在第一个例子的基础上加了回调,如果是用AsyncTask或者Handler的话,可以在回调内做更新界面的操作。最关键的部分在于线程的detach方法,它将持有Activity引用的回调成员置空。那么我们只要保证在Activity被销毁前,所有持有该引用的变量都能被置空,这样,长时间持有Activity引用的条件也就不成立了。所以可以看到在Activity的onDestroy方法和new一个线程之前,都要将现有线程的回调置空。

二、使用MAT检测内存泄漏

MAT是eclipse的内存分析工具。接下来我会从安装开始,并结合上面的例子来简单介绍一下如何使用这个工具。

1、安装

Help--> Install New Software --> Add,链接是http://download.eclipse.org/mat/1.3/update-site/,最好要勾选“Contact all update sites...”这个选项,不然你的eclipse可能没有这个插件依赖的一些包。点击确定就等它龟速下载吧。安装完之后重启eclipse就可以用了。

2、使用

这个工具可以结合DDMS来用。先将我们的程序跑起来,然后打开DDMS,选择要分析的程序,点击Dump HPROF File按钮,稍等片刻就可以看到内存的详细使用情况了。

3、结合上面的例子分析

我们首先看一下不置空回调的情况,把ondestroy和new线程那部分代码注释掉。运行程序之后打开DDMS,并开启Update Heap(为了能使用GC)和Update Thread(查看线程)。之后我们运行线程,并切换横屏,如下图:

可以看到此时多了一个线程(图中ID为9)。打开MAT,点击"Open Dominator Tree for entrie heap",最后再点开“Show as Histogram”,在这里就可以查看我们各个类的引用情况了。如下图:

可以看到此时的MainActivity有两个引用。返回到DDMS,点击“Cause GC”(那个垃圾桶图标),打开新的MAT,继续找MainActivity,如果没有意外出现的话,MainActivity的引用仍为2,也就是说,并没有回收前一个Activity。

但是如果取消刚才的注释,同样的操作,点击GC之后,发现MainActivity的引用又变回1了。

注意:以上操作最好在线程执行完毕之前完成,不熟悉的可以将线程执行时间设置长一些来看效果。

三、建议

1、尽量不要长时间保持Activity的引用。

2、尝试用Application的context来代替Activity的context。因为Application是存在于程序的整个生命周期的,不像Activity一样随时有可能被销毁。

3、避免使用静态的持有Activity引用的成员变量,巧妙使用静态内部类。

4、适当运用弱引用。

5、如果确实需要保持对Activity的引用,必须确保在Activity的生命周期结束前,取消该引用。

时间: 2024-10-28 21:32:20

Android防止内存泄漏以及MAT的使用的相关文章

[Android Memory] 内存分析工具 MAT 的使用

转载自: http://blog.csdn.net/aaa2832/article/details/19419679 1 内存泄漏的排查方法 Dalvik Debug Monitor Server (DDMS) 是 ADT插件的一部分,其中有两项功能可用于内存检查 : ·    heap 查看堆的分配情况 ·    allocation tracker跟踪内存分配情况 DDMS 这两项功能有助于找到内存泄漏的操作行为. Eclipse Memory Analysis Tools (MAT) 是一

Android 常见内存泄漏的解决方式

在Android程序开发中.当一个对象已经不须要再使用了,本该被回收时.而另外一个正在使用的对象持有它的引用从而导致它不能被回收.这就导致本该被回收的对象不能被回收而停留在堆内存中,内存泄漏就产生了. 内存泄漏有什么影响呢? 它是造成应用程序OOM的主要原因之中的一个.由于Android系统为每一个应用程序分配的内存有限.当一个应用中产生的内存泄漏比較多时.就难免会导致应用所须要的内存超过这个系统分配的内存限额,这就造成了内存溢出而导致应用Crash. 一.单例造成的内存泄漏 Android的单

RxJava在Android中内存泄漏解决以及RxJava的封装

本文转自:http://blog.csdn.net/adzcsx2 RxJava在现在是一个非常前卫的异步框架,也是由于他非常新,所以比较难以驾驭. 像okhttp直接在onStop或者onDestroy 调用它的cancel方法就行了,但是Rxjava并没有那么简单. 因为假如每次请求都得到Observable对象,然后再onStop中unsubscribe取消,这样很不利于封装.而且会造成代码量很多,所以我找到了用rxlifecycle的解决方案. 先导包 compile 'com.trel

Android Native内存泄漏检测方法

Android 检测 C/C++内存泄漏的方法越来越简便了,下面列举一下不同场景下检测C/C++内存泄漏的方法. Android O(针对root设备,调试APP) 1. 准备一个userdebug或eng版本手机,下载native_heapdump_viewer.py脚本备用 2. 执行以下命令 adb shell setprop wrap.<APP_PACKAGE_NAME> '"LIBC_DEBUG_MALLOC_OPTIONS=backtrace"' 3. 执行重现

Android Handler 内存泄漏问题

1. 问题先看以下代码: 第一种写法: public class MainActivity extends AppCompatActivity { ... ... ... private class MyHandler extends Handler { @Override public void handleMessage(@NonNull Message msg) { if (msg.what == 10086) { mTv.setText(String.valueOf(msg.arg1))

RxJava在Android中内存泄漏解决以及RxJava的封装。

RxJava在现在是一个非常前卫的异步框架,也是由于他非常新,所以比较难以驾驭. 像okhttp直接在onStop或者onDestroy 调用它的cancel方法就行了,但是Rxjava并没有那么简单. 因为假如每次请求都得到Observable对象,然后再onStop中unsubscribe取消,这样很不利于封装.而且会造成代码量很多,所以我找到了用rxlifecycle的解决方案. 先导包 compile 'com.trello:rxlifecycle:0.5.0' compile 'com

Android内存泄漏检测与MAT使用

内存泄漏基本概念 内存检测这部分,相关的知识有JVM虚拟机垃圾收集机制,类加载机制,内存模型等.编写没有内存泄漏的程序,对提高程序稳定性,提高用户体验具有重要的意义.因此,学习java利用java编写程序的时候,要特别注意内存泄漏相关的问题.虽然JVM提供了自动垃圾回收机制,但是还是有很多情况会导致内存泄漏. 内存泄漏主要原因就是一个生命周期长的对象,持有了一个生命周期短的对象的引用.这样,会导致短的对象在该回收时候无法被回收.Android中比较典型的有:1.静态变量持有Activity的co

利用 LeakCanary 来检查 Android 内存泄漏

前言 你被概率性的 OOM 困扰么?有时候,OOM 像幽灵一样,挥之不去,可真想把它揪出来时,又捉之不着.或许,是时候用 LeakCanary 来诊断一下了.它是一个用来检查 Android 下内存泄漏的开源库,这篇文章主要介绍其用法.架构和其背后的实现原理. Square 有篇文章介绍了开发这个库的原因.他们的一个付款流程里,需要用到用户的签名,他们直接用 Bitmap 来画签名,Bitmap 大小和屏幕分辨率是一样的.问题来了,在试图创建这个 Bitmap 对象时,概率性 OOM 如幽灵般相

Android —— 内存泄漏检查

今天地铁上看到一篇不错的将内存泄漏简单检查的文章,觉得还不错哟,内存泄漏确实是每个程序员头疼的事情,这里就多研究一下咯^^ 一. 常见的垃圾回收算法 参看文章 引用计数法 引用计数法基本上最简单的垃圾回收策略,它的核心思想是: 当有指针指向某实例时,计数加一, 当删除一个指针时,计数减一,当计数为0时,说明该实例没有引用可以被垃圾回收器回收. 这种回收策略的缺点是显而易见的: 1.维护引用计数是有开销的 2.计数的保存会消耗额外的空间 3.无法处理循环引用 标记清除 标记清除,顾名思义分为2步: