内存泄漏

getContext != getApplicationContext

View.getContext(): Returns the context the view is currently running in. Usually the currently active Activity.
所以如果用了getContext的话,就可能造成Activity的内存泄漏

内部类,内部类,内部类

内部类会默认带上外部类的引用

外部类方法参数

内部类不光带上外部类引用,如果在内部类里还引用了外部类方法参数,该参数也会在内部类中别引用。

class outer {
    void method(Context context) {
        Inner inner =new Inner {
            void innerMethod() {
            //这里使用了外部类参数context,
                context.getString(); 
            }
        }
    }
}

单例,单例,单例

单例对象尤其要关注其引用的对象,

注意非静态内部类的静态实例

public class TestActivity extends Activity{
    static Up up;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(null == up) {
            up = new Up();
        }
    }
    class Up{
    }
}

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

注意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);   
}

以上代码在android 2.3系统上,它会导致activity销毁后无法被系统回收。label .setBackgroundDrawable函数调用会将label赋值给sBackground的成员变量mCallback。上面代码意味着:sBackground(GC Root)会持有TextView对象,而TextView持有Activity对象。所以导致Activity对象无法被系统回收。
先看看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)。

注册某个对象后未反注册

注册广播接收器、注册观察者等等

使用RxJava操作符cache

RxJava的第三方扩展包很多,比如RxAndroid,里面含有很多和视图绑定的东西,让我们操作起来更简单,这些东西本身来说是weak的,但是一旦使用了cache操作符,cache前的observable即便和生命周期绑定,也不能被正常回收,所以cache前一定要绑定生命周期

ListView item没有复用

Bitmap没有回收

图片占用内存是指在Navtive中占用的内存,当然BitMap使用的绝大多数内存就是该内存。
因为我们可以简单的认为它就是BitMap所占用的内存。
Bitmap对象在不使用时,我们应该先调用recycle(),然后才它设置为null.
虽然Bitmap在被回收时可以通过BitmapFinalizer来回收内存。但是调用recycle()是一个良好的习惯
在Android4.0之前,Bitmap的内存是分配在Native堆中,调用recycle()可以立即释放Native内存。
从Android4.0开始,Bitmap的内存就是分配在dalvik堆中,即JAVA堆中的,调用recycle()并不能立即释放Native内存。但是调用recycle()也是一个良好的习惯。

资源对象没关闭造成的内存泄露

源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。比如cursor调用close,因为它们的缓冲不仅存在于Java虚拟机内,还存在于Java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄露。

CookieSyncManager

CookieSyncManager 是个全局静态单例,操作系统内部使用了 App 的 Activity 作为 Context 构造了它的实例。我们应该在 App 启动的时候,抢先帮系统创建这个单例,而且要用 applicationContext,让它不会引用到 Activity。

handler内存问题

由内部类说起

内部类会默认的在其对象中增加一个其外部对象的引用。这就是为什么内部类可以直接使用到外部类的资源。这还包括匿名内部类。在Activity中比较典型的用法是:

public class MainActivity extends AppCompatActivity {

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

这个时候mhandler对象保留了对外层MainActivity的引用。
在android的Handler源码中有如下一步

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this; //msg引用了mhandler
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis); //queue又引用了msg
    }

引用关系图

这里写图片描述

而作为主线程的looper是会一直存在的。所以最终的结果就是:
如果在MainActivity被finish后,并且还存在没有被处理的消息,那么这个MainActivity会一直被强引用着而不能被回收,这就是内存泄漏。

引申开来:

凡是内部类被一个长期运行的后台服务所引用,那么该外部类也会被一直引用而不能回收。所以使用内部类要格外关注这一点。

解决的办法:

回到handler带来的问题,

onDestroy时候remove所有msg

Activity finish后未处理的msg是问题根源,所以清空所有未被执行的msg.

使用静态内部类 + weakReference

静态内部类不会保留对外部类的引用,如果一定要引用外部类,使用weakReference

四种引用类型说明

这里写图片描述

虚引用的补充说明

“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
ReferenceQueue queue = new ReferenceQueue ();
PhantomReference pr = new PhantomReference (object, queue);
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。(finalize被触发的源头?)

参考

http://www.tuicool.com/articles/7Zzuma

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值