如果一个对象只具有弱引用(就是说弱引用指向了某个对象,但只要该对象不是强引用或没有被强引用指向),那么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。弱引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

简单解释(以Activity为例)

在Activity中使用WeakReference<Activity>弱引用,Activity没有退出的情况下(在执行生命周期onDestroy()之前),弱引用对象WeakReference<Activity>被强引用Activity指向,该对象不会被GC检查时回收掉;在Activity退出的情况下(在执行生命周期onDestroy()之后),弱引用对象WeakReference<Activity>没有被强引用Activity指向,该对象就会被GC检查时回收掉。

什么是内存泄漏?

Java使用有向图机制,通过GC自动检查内存中的对象;如果GC发现一个或一组对象为不可达的状态,则将该对象从内存中回收。也就是说:一个对象不被任何引用所指向,则该对象会在被GC发现的时候回收。

可能导致内存泄漏的实例:


Handler造成的内存泄漏
Handler的使用造成的内存泄漏问题应该说最为常见了,平时在处理网络任务或者封装一些请求回调等api都应该会借助Handler来处理,对于Handler的使用代码编写一不规范即有可能造成内存泄漏,如下示例:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        requestData();
    }


    private void requestData(){
        //在子线程中请求数据(就不写了),然后把数据发送到UI线程
        Message message = Message.obtain();
        mHandler.sendMessage(message);
    }


     private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
            //...
            }
        };
}


当使用内部类(或者匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类的对象(通常是Activity)的引用(否则怎么可能通过Handler来操作Activity的View?)。而Handler通常会伴随着一个耗时的后台线程(比如:拉取网络图片);该后台线程在任务执行完毕后,通过消息机制通知Handler,然后Handler把图片更新到界面上。假设用户在网络请求过程中关闭了Activity,正常情况下这个Activity不再被使用,就有可能被GC回收;但此时线程尚未执行完毕,而该线程持有Handler的引用(不然怎么发送消息给Handler?),Handler又持有Activity的引用,就导致该Activity无法被回收(内存泄漏),直到网络请求结束(如:图片下载完毕)。另外如果执行了Handler的postDelayed(),该方法会将Handler装入一个Message,并把该Message推到MessageQueue中,由此产生了一条链式结构:MessageQueue->Message->Handler->Activity,导致Activity被持有引用而无法被回收(总结:实例对象Activity被其他对象Handler持有引用,而无法被回收)。

内存泄漏的危害是什么?

内存泄漏会引发虚拟机占用内存过高。对于Android应用程序来说,用户打开一个Activity,使用完之后关闭,内存泄漏;执行上述步骤多次,程序占用内存超过系统限制。

如何避免内存泄漏?可以使用什么方法?

由此引出了弱引用。用于非必需对象,被弱引用关联的对象只能生存到下一次GC发生之前。当GC工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象(就是说弱引用指向了某个对象,但只要该对象不是强引用或没有被强引用指向)。


Handler内存泄漏问题解决(1.静态的内部类。2.使用弱引用。3.MainActivity退出的时候,移除消息队列中所有消息和所有的Runnable。)

public class MainActivity extends AppCompatActivity {
    private MineHandler mHandler = new MineHandler(this);
    private TextView mTextView ;

    //1.静态的内部类
    private static class MineHandler extends Handler {

        //2.使用弱引用
        private WeakReference<MainActivity> reference;
        public MineHandler(MainActivity activity) {
                reference = new WeakReference<MainActivity>(activity);
        }


        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = (MainActivity) reference.get();
            if(activity != null){
            activity.mTextView.setText("");
            }
        }
    }
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView)findViewById(R.id.textview);
        requestData();
    }
 
    private void requestData() {
        //在子线程中请求数据(就不写了),然后把数据发送到UI线程
        Message message = Message.obtain();
        mHandler.sendMessage(message);
    }
 
    @Override
    protected void onDestroy() {

        //3.MainActivity退出的时候,移除消息队列中所有消息和所有的Runnable。
        mHandler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }
}


使用了上述代码后,在Activity退出的情况下(在执行生命周期onDestroy()之后),就算后台线程还没有结束,但由于仅有一个来自Handler的弱引用指向MainActivity,GC就会在检查的时候把WeakReference<MainActivity>回收掉。

另外,关于内存泄漏问题,可以参考博客Android内存泄漏详解和总结_ErwinNakajima的博客-CSDN博客,关于强引用、软引用、弱引用和虚引用的使用和正确理解,可以参考博客Android 强引用、软引用、弱引用和虚引用的使用和正确理解_ErwinNakajima的博客-CSDN博客_android 强软弱虚

如对此有疑问,请联系qq1164688204。

推荐Android开源项目

项目功能介绍:RxJava2和Retrofit2项目,添加自动管理token功能,添加RxJava2生命周期管理,使用App架构设计是MVP模式和MVVM模式,同时使用组件化,部分代码使用Kotlin,此项目持续维护中。

项目地址:https://gitee.com/urasaki/RxJava2AndRetrofit2

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐