Android AndroidStudio MAT LeakCanary 内存分析之 初识内存泄漏

Android AndroidStudio MAT LeakCanary 内存分析之 初识内存泄漏
http://blog.csdn.net/qq_28195645/article/details/51733342

Android AndroidStudio MAT LeakCanary 内存分析之 AndroidStudio 内存泄漏分析 Memory Monitor
http://blog.csdn.net/qq_28195645/article/details/51734506

Android AndroidStudio MAT LeakCanary 内存分析之 LeakCanary
http://blog.csdn.net/qq_28195645/article/details/51734987

Android AndroidStudio MAT LeakCanary 内存分析之 DDMS+MAT
http://blog.csdn.net/qq_28195645/article/details/51735522

说起Android进阶、那么必须谈一谈内存泄漏。那么现在检测app内存泄漏的办法有很多 比如 :
1、DDMS + MAT http://www.eclipse.org/mat/downloads.php
2、leakcanary https://github.com/square/leakcanary
3、AndroidStudio 1.5+ Memory Monitor https://developer.android.com/studio/profile/am-memory.html
题外:Android性能分析工具——TraceView
实际开发中、常用的就是leakcanary,本系列我们会将这三种办法都有涉及

首先什么是内存泄漏、常引起内存泄漏的code

这里写图片描述

如上图、如果你要删除对象B,这也将释放它所支配的对象(C,D,E,F)所使用的内存.事实上,如果对象C,D,E和F被标记为删除,但对象B还是指他们,这就是他们没有释放的原因。

Java内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到gc roots导致无法被GC回收。无用的对象占据着内存空间,使得实际可使用内存变小,形象地说法就是内存泄漏了。而内存泄漏出现的原因就是存在了无效的引用,导致本来需要被GC的对象没有被回收掉。这样会导致一直占用着内存。下面分析一些可能导致内存泄漏的情景。如果想了解更多就百度吧

1、非静态内部类的静态实例容易造成内存泄漏

    private static Leak mLeak;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        mLeak = new Leak();
    }

    class Leak {
    }

上面的代码中的mLeak实例类型为静态实例,在第一个MainActivity act1实例创建时,mLeak会获得并一直持有act1的引用。(原因:mLeak是存储在静态区的静态变量,而Leak是内部类,其持有外部类Activity的引用。)当MainAcitivity销毁后重建,因为mLeak持有act1的引用,所以act1是无法被GC回收的,进程中会存在2个MainActivity实例(act1和重建后的MainActivity实例),这个act1对象就是一个无用的但一直占用内存的对象,即无法回收的垃圾对象。所以,对于lauchMode不是singleInstance的Activity, 应该避免在activity里面实例化其非静态内部类的静态实例。

2、activity使用静态成员

private static Drawable sBackground;    
@Override    
protected void onCreate(Bundle state) {    
    super.onCreate(state);    

    TextView label = new TextView(this);    
    label.setText("Leaks are bad");    

    if (sBackground == null) {    
        sBackground = getDrawable(R.drawable.large_bitmap);    
    }    
    label.setBackgroundDrawable(sBackground);    

    setContentView(label);    
}   

由于用静态成员sBackground 缓存了drawable对象,所以activity加载速度会加快,但是这样做是错误的。因为在Android 2.3系统上,它会导致activity销毁后无法被系统回收。

label .setBackgroundDrawable函数调用会将label赋值给sBackground的成员变量mCallback。

上面代码意味着:sBackground(GC Root)会持有TextView对象,而TextView持有Activity对象。所以导致Activity对象无法被系统回收。

下面看看android4.0为了避免上述问题所做的改进。

先看看android 2.3的Drawable.Java对setCallback的实现:

public final void setCallback(Callback cb){

    mCallback = cb;

}

再看看android 4.0的Drawable.Java对setCallback的实现:

public final void setCallback(Callback cb){

    mCallback = newWeakReference<Callback> (cb);

}

在android 2.3中要避免内存泄漏也是可以做到的, 在activity的onDestroy时调用

sBackgroundDrawable.setCallback(null)。

以上2个例子的内存泄漏都是因为Activity的引用的生命周期超越了activity对象的生命周期。也就是常说的Context泄漏,因为activity就是context。

想要避免context相关的内存泄漏,需要注意以下几点:

·不要对activity的context长期引用(一个activity的引用的生存周期应该和activity的生命周期相同)

·如果可以的话,尽量使用关于application的context来替代和activity相关的context

·如果一个acitivity的非静态内部类的生命周期不受控制,那么避免使用它;正确的方法是使用一个静态的内部类,并且对它的外部类有一WeakReference,就像在ViewRootImpl中内部类W所做的那样。

3、使用handler时的内存问题

public class HandlerActivity extends Activity {  

    private final Handler mHandler = new Handler() {  
        @Override  
        public void handleMessage(Message msg) {  
            // ...  
        }  
    };  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        mHandler.sendMessageDelayed(Message.obtain(), 60000);  
        //just finish this activity  
        finish();  
    }  
}  

以上的写法会出现这样的error提示:This Handler class should be static or leaks might occur

1、Handler 的生命周期与Activity 不一致

  • 当Android应用启动的时候,会先创建一个UI主线程的Looper对象,Looper实现了一个简单的消息队列,一个一个的处理里面的Message对象。主线程Looper对象在整个应用生命周期中存在。
  • 当在主线程中初始化Handler时,该Handler和Looper的消息队列关联(没有关联会报错的)。发送到消息队列的Message会引用发送该消息的Handler对象,这样系统可以调用 Handler#handleMessage(Message) 来分发处理该消息。

2、handler 引用 Activity 阻止了GC对Acivity的回收

在Java中,非静态(匿名)内部类会默认隐性引用外部类对象。而静态内部类不会引用外部类对象。
如果外部类是Activity,则会引起Activity泄露 。

当Activity finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。

避免handler导致的内存泄漏

  • 使用显形的引用,1.静态内部类。 2. 外部类
  • 使用弱引用 2. WeakReference
  • android-weak-handler https://github.com/badoo/android-weak-handler
  • Handler可以使用removeCallbacksAndMessages(null),它将移除这个Handler所拥有的Runnable与Message。

修改代码如下

public class HandlerActivity2 extends Activity {  
    private static final int MESSAGE_1 = 1;  
    private static final int MESSAGE_2 = 2;  
    private static final int MESSAGE_3 = 3;  
    private final Handler mHandler = new MyHandler(this);  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        mHandler.sendMessageDelayed(Message.obtain(), 60000);  

        // just finish this activity  
        finish();  
    }  

    public void todo() {  
    };  

    private static class MyHandler extends Handler {  
        private final WeakReference<HandlerActivity2> mActivity;  

        public MyHandler(HandlerActivity2 activity) {  
            mActivity = new WeakReference<HandlerActivity2>(activity);  
        }  

        @Override  
        public void handleMessage(Message msg) {  
            System.out.println(msg);  
            if (mActivity.get() == null) {  
                return;  
            }  
            mActivity.get().todo();  
        }  
    }  
    /**
    *  当Activity finish后 handler对象还是在Message中排队。 还是会处理消息,这些处理有必要?
    *正常Activitiy finish后,已经没有必要对消息处理,那需要怎么做呢?
    *解决方案也很简单,在Activity onStop或者onDestroy的时候,取消掉该Handler对象的Message和   Runnable。
    *通过查看Handler的API,它有几个方法:removeCallbacks(Runnable r)和removeMessages(int what)等。
    */

    @Override  
    public void onDestroy() {  
        //  If null, all callbacks and messages will be removed.  
        mHandler.removeCallbacksAndMessages(null);  
    }  
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值