zoukankan      html  css  js  c++  java
  • 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的上下文。




  • 相关阅读:
    openwrt 相关文章
    负载均衡相关文章
    Today's Progress
    Rodrigues formula is beautiful, but uneven to sine and cosine. (zz Berkeley's Page)
    Camera Calibration in detail
    Fundamental Matrix in Epipolar
    Camera Calibration's fx and fy do Cares in SLAM
    FilterEngine::apply
    FilterEngine 类解析——OpenCV图像滤波核心引擎(zz)
    gaussBlur
  • 原文地址:https://www.cnblogs.com/ldq2016/p/8472913.html
Copyright © 2011-2022 走看看