• Android内存优化10 内存泄漏常见情况1 静态泄漏


    1,内存泄漏到本质是该释放的对象被持久化的对象引用了,造成持久化的常见情况有1,静态持久化 2,线程持久化

    线程持久化

    因为存活的线程是有dvk虚拟久直接持有,所以存活的线程都是持久化的

    内存泄漏1:静态Activities(static Activities)

    代码如下:
    MainActivity.Java

    public class MainActivity extends AppCompatActivity {
        private static MainActivity activity;
        TextView saButton;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            saButton = (TextView) findViewById(R.id.text);
            saButton.setOnClickListener(new View.OnClickListener() {
                @Override public void onClick(View v) {
                    setStaticActivity();
                    nextActivity();
                }
            });
        }
        void setStaticActivity() {
            activity = this;
        }
    
        void nextActivity(){
            startActivity(new Intent(this,RegisterActivity.class));
            SystemClock.sleep(1000);
            finish();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            //使用LeakCanary观察是否有内存泄漏
            MyApplication.getRefWatcher().watch(this);
        }
    }

    LeakCanary检测出的内存泄漏:

    这里写图片描述

    为什么?
    在上面代码中,我们声明了一个静态的Activity变量并且在TextView的OnClick事件里引用了当前正在运行的Activity实例,所以如果在activity的生命周期结束之前没有清除这个引用,则会引起内存泄漏。因为声明的activity是静态的,会常驻内存,如果该对象不清除,则垃圾回收器无法回收变量。

    怎么解决?
    最简单的方法是在onDestory方法中将静态变量activity置空,这样垃圾回收器就可以将静态变量回收。

    @Override
        protected void onDestroy() {
            super.onDestroy();
            activity = null;
            //使用LeakCanary观察是否有内存泄漏
            MyApplication.getRefWatcher().watch(this);
        }

     不使用静态activity,或给静态activity赋值时,考虑赋值的activity生命周期是不是全局的,或者在静态activity使用完后及时释放

    内存泄漏2:静态View

    代码如下:
    MainActivity.java

        ...
        private static View view;
        TextView saButton;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            saButton = (TextView) findViewById(R.id.text);
            saButton.setOnClickListener(new View.OnClickListener() {
                @Override public void onClick(View v) {
                    setStaticView();
                    nextActivity();
                }
            });
        }
        void setStaticView() {
            view = findViewById(R.id.sv_view);
        }
        ...

    LeakCanary检测到的内存泄漏

    这里写图片描述

    为什么?
    上面代码看似没有问题,在Activity里声明一个静态变量view,然后初始化,当Activity生命周期结束了内存也释放了,但是LeakCanary却显示出现了内存泄漏,为什么?问题出在这里,View一旦被加载到界面中将会持有一个Context对象的引用,在这个例子中,这个context对象是我们的Activity,声明一个静态变量引用这个View,也就引用了activity,所以当activity生命周期结束了,静态View没有清除掉,还持有activity的引用,因此内存泄漏了。

    怎么解决?
    在onDestroy方法里将静态变量置空。

    @Override
    protected void onDestroy() {
        super.onDestroy();
        view = null;
        MyApplication.getRefWatcher().watch(this);
    } 
    不使用静态view,或在activity关闭时将静态view赋值为null

    内存泄漏3:静态内部类

    代码如下:
    MainActivity.java

    private static Object inner;
    void createInnerClass() {
        class InnerClass {
        }
        inner = new InnerClass();
    }
    
    View icButton = findViewById(R.id.ic_button);
    icButton.setOnClickListener(new View.OnClickListener() {
        @Override public void onClick(View v) {
            createInnerClass();
            nextActivity();
        }
    });
    
     

    使用LeakCanary检测到的内存泄漏:

    这里写图片描述

    为什么?
    非静态内部类会持有外部类的引用,在上面代码中内部类持有Activity的引用,因此inner会一直持有Activity,如果Activity生命周期结束没有清除这个引用,这样就发生了内存泄漏。

    怎么解决?
    因为非静态内部类隐式持有外部类的强引用,所以我们将内部类声明成静态的就可以了。

    void createInnerClass() {
        static class InnerClass {
        }
        inner = new InnerClass();
    }
     

    内存泄漏3:静态Drawable

    当一个Drawable附加到一个 View上时,
    View会将其作为一个callback设定到Drawable上。意味着Drawable拥有一个View的引用,上面说了view会有上下文的引用

    内存泄漏4:静态集合中对象没清理造成的内存泄漏

    我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。

     

    内存泄漏5:单例导致内存泄露

    单例模式在Android开发中会经常用到,但是如果使用不当就会导致内存泄露。因为单例的静态特性使得它的生命周期同应用的生命周期一样长,如果一个对象已经没有用处了,但是单例还持有它的引用,那么在整个应用程序的生命周期它都不能正常被回收,从而导致内存泄露。

    public class AppSettings {
    
        private static AppSettings sInstance;
        private Context mContext;
    
        private AppSettings(Context context) {
            this.mContext = context;
        }
    
        public static AppSettings getInstance(Context context) {
            if (sInstance == null) {
                sInstance = new AppSettings(context);
            }
            return sInstance;
        }
    }
    

    像上面代码中这样的单例,如果我们在调用getInstance(Context context)方法的时候传入的context参数是ActivityService等上下文,就会导致内存泄露。

    Activity为例,当我们启动一个Activity,并调用getInstance(Context context)方法去获取AppSettings的单例,传入Activity.this作为context,这样AppSettings类的单例sInstance就持有了Activity的引用,当我们退出Activity时,该Activity就没有用了,但是因为sIntance作为静态单例(在应用程序的整个生命周期中存在)会继续持有这个Activity的引用,导致这个Activity对象无法被回收释放,这就造成了内存泄露。

    为了避免这样单例导致内存泄露,我们可以将context参数改为全局的上下文:

    private AppSettings(Context context) {
        this.mContext = context.getApplicationContext();
    }
    

    全局的上下文Application Context就是应用程序的上下文,和单例的生命周期一样长,这样就避免了内存泄漏。

    单例模式对应应用程序的生命周期,所以我们在构造单例的时候尽量避免使用Activity的上下文,而是使用Application的上下文。




  • 相关阅读:
    [大话数据结构]线性表之单链表结构和顺序存储结构
    [大话数据结构]算法
    [C#编程参考]把图像转换为数组的两种实现
    [C#绘图]在半透明矩形上绘制字符串
    [C#绘图]Matrix类
    在C#调用C++的DLL方法(二)生成托管的DLL
    在C#调用C++的DLL方法(一)生成非托管dll
    彻底解决 LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
    修复./mysql/proc
    linux 网络连接数查看方法
  • 原文地址:https://www.cnblogs.com/ldq2016/p/8472913.html
Copyright © 2020-2023  润新知