android 常见的内存问题

在面试中,经常会被问到内存优化或者内存泄漏的相关概念,这些老生常谈的问题,你是真的懂了吗?

笔者今天就想要彻底搞明白这些,还清当初欠下的技术债。

内存泄漏问题:

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

常见的场景:

  • 非静态内部类,比如handler使用问题。
  • 静态的成员变量的使用
  • 静态类持有activity的引用
  • 各种回调,监听,资源使用在onDestory需要置空

非静态内部类:

通常使用handler,我们会使用一个主线程 ,new handler 然后重写handlerMessage()就完了,

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

这样就是会造成内存的泄漏,分析一下:

这个handler其实是一个inner class,不是static,这样这个handler 会默认持有 activity的引用,并且在使用非静态内部类的时候,会先创建外部类activity的对象,mHandler 不能独立存在,而静态的内部类是可以不依赖外部类的就被创建的,先列举一下static inner class 和no static inner class的区别

class对比 static inner class non-static inner class
与外部class引用关系 如果没有传入参数,就无引用关系 自动获得强引用(implicit reference)
被调用时需要外部实例 不需要(比如Bulider类) 需要
能否调用外部class中的变量与方法 不能
生命周期 自主的生命周期 依赖于外部类,甚至比外部类更长

 假如我们在A界面用了非静态内部类的handler,此时finish掉A跳转到B界面,但A中的handler里面还有消息在处理,导致 handler无法释放,而handler没有被释放却又持有了外部类的activity,则activity A也不能释放,这样反复多次后,就会造成A占用本该释放的内存无法释放使得内存不够用,形成memoryLeak。

处理方式:写出静态的内部类,并通过弱引用(会在下一次GC时释放相关联的对象)持有外部引用,

 public static class MyHandler extends Handler {
        private  WeakReference<MainActivity> mActivity;

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

        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = mActivity.get();
            if (activity != null) {
                // ...
            }
        }
    }

,另外,对于handler,我们可以在onDestory()中,调用removeCallbacksAndMessages清空消息队列,使得handler得以释放

扫描二维码关注公众号,回复: 5657079 查看本文章
  /**
     * Remove any pending posts of callbacks and sent messages whose
     * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
     * all callbacks and messages will be removed.
     */
    public final void removeCallbacksAndMessages(Object token) {
        mQueue.removeCallbacksAndMessages(this, token);
    }

这种方式是一种手段对于handler而言,最好还是静态内部类弱引用吧。

静态的成员变量的使用

静态变量的生命周期是和类加载机制相关,当类被加载后,变量就会被创建,直到类加载器classsLoader被卸载才会回收。当acitivity中存在静态变量后,即使这个acitivity被回收,这个静态变量无法被回收,那这个静态变量占用的内存是不是就相当于被泄漏了呢?或者重复多次,就会创建多个这个变量,然后就有多份这个静态变量占用的内存没被回收?

这种情况算不算内存泄漏呢?按照定义是的,实际上不是那么回事。想想static 这个修饰符,静态变量是类变量,不是类实例变量,不依赖具体对象,只会创造一份,并不会创建多份,即使你多次创建activity,这个静态变量依旧只有一份。现在想想人家staitc 本来就不想随着对象回收而被回收,但内存泄漏研究的是实例对象,压根不挨着,所以静态变量使用造成内存泄漏只是理论上的,实际上微乎其微,因为照这样说,就不要用static 了,不用 public static final Sting TAG = ""; 这种定义了。

注意:这里的静态成员变量和上面的静态内部类是不冲突的,一个是变量,一个是类;一个是说静态变量自己不能被回收,activity是可以回收的,一个是说handler不能被回收导致acitivity不能被回收,二者是不一样的。这里非常具有迷惑性,我也是花费好久搞明白

静态类持有activity的引用

比如单例模式,因为单例模式用了static 修饰自己,这样生命周期和application一样了,当我们在创建单例对象的时候如果传入了acitivity的context,那么当acitivity被释放的时候应该被回收,却因为被单例对象持有其context无法被释放,这样就造成了acitvity占用的内存不能被释放。这样来类比,就是一些静态类持有了经常需要释放的对象的引用后,就会发生内存泄漏。

各种回调,监听,资源使用在onDestory需要置空

比如一些广播的注册,EventBus,butterKnife 第三方框架之类的,或者一些流的操作,file ,http连接,最好需要断开连接,对象置空。

猜你喜欢

转载自blog.csdn.net/sjh_389510506/article/details/88615050