zoukankan      html  css  js  c++  java
  • Android开发——常见的内存泄漏以及解决方案(一)

    0. 前言  

    转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52333954

    Android的内存泄漏是Android开发领域永恒的话题,那今天就总结一下常见的内存泄漏吧。也给自己提个醒,在以后的编码过程中多注意这个问题。在Android Studio里可以通过一些分析工具比如MAT来找出潜在的内存泄漏,在Android Device Monitor中对相应进行进行Dump HPROF File,并对这个文件在SDK的platform-tools目录下进行<hprof-conv infilePath outfileName>即可在platform-tools目录下生成使用MAT工具查看的hprof文件

    还有如果不清楚Java里的OOM、以及内存泄漏和内存溢出区别的小伙伴,可以参考我之前写过的Java技术——Java中的内存泄漏

    此篇将从静态变量引用Activity、匿名类、内部类、Handler、以及监听器等方面的实例说明内存泄漏的发生原因以及解决方案。


    1. 单例模式导致内存泄漏(实质是静态变量引用Activity

    如果不了解单例模式的小伙伴可以查看我之前写过的设计模式——单例模式解析已经对单例模式分析的很清楚了。这里就不多赘述了。

    单例由于其静态的特性使得其生命周期跟应用一样长,处理不当极易导致内存泄漏。

    public class SingleUtils {
        private static SingleUtils mInstance = null;
        private Context context;
        private SingleUtils (Context context){
            this.context = context;
        }
    
        public static SingleUtils getInstance(Context context){
            if(mInstance == null){
                mInstance = new SingleUtils (context);
            }
            return mInstance;
        }
    
    public Object getObject(){//根据业务逻辑传入参数
        //返回业务逻辑结果,这里需要用到context
        }
    }
    

    如果你看了上面链接文中介绍的单例模式,那么就很容易理解下面单例类中返回实例的代码造成了内存泄漏,我们传入了上下文context(传入上下文是为了实现单例类中的业务逻辑),这个上下文可能是Android应用中的一个Activity界面,当它finish掉的时候,这个单例类的静态对象拥有了这个Activity的引用,静态变量是驻扎在JVM的方法区,静态变量引用的对象是不会被GC回收的,因为它们所引用的对象本身就是GC ROOT。导致该Activty一直驻留在内存中,并发生内存泄漏。

     

    解决方案:

    在单例中我们尽可能的引用生命周期较长的对象,将第10行代码修改如下即可。

    mInstance = new SingleUtils (context.getApplicationContext());


    2. 内部类导致内存泄漏

    非静态内部类会持有外部类的引用,如果我们在一个外部类中定义一个静态变量,这个静态变量是引用内部类对象,内部类能够引用外部类的成员这一优势,就是通过持有外部类的引用来实现的,但是这将会导致内存泄漏,因为这相当于间接导致静态引用外部类

    public class MyActivity extends Activity {
        //非静态内部类User创建的静态实例mUser
        private static InnerClass mInnerClass = null;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            mInnerClass = new InnerClass();
        }
         class InnerClass{
        }
    }
    


    解决方案:

    1)在onDestroy方法中调用mInnerClass的判空,若不为空,手动置为null即可。

    2)将内部类定义为静态内部类,使其不能与外部类建立关系。 


    3.匿名导致内存泄漏

    匿名内部类同样会持有一个外部类的引用,因此如果在Activity内定义了一个匿名的AsyncTask对象。如果Activity被销毁之后AsyncTask仍然在执行,那就会组织垃圾回收器回收Activity对象,进而导致内存泄漏,直到执行结束才能回收Activity

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                //子线程中持有Activity的引用
                //子线程在Activity销毁后依然会继续执行,导致该Activity内存泄漏
                while (true) ;
            }
        }.execute();
    }
    


    解决方案:

    1)在onDestroy中中断子线程的运行。

    2)由于线程池利于管理,可以使用全局的线程池代替在类中创建子线程。


    4Handler导致内存泄漏

    定义一个匿名的Runnable对象会间接地引用定义它的Activity对象,而它会被提交到HandlerMessageQueue中,如果它在Activity销毁时还没有被处理(如下例中延迟时间执行),那就会导致内存泄漏了。

    private final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
    
        }
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
        handler.postDelayed(new Runnable() {
            @Override
            public void run() { /* ... */ }
        }, Integer.MAX_VALUE); 
    }
    

    解决方案:

    1)在onDestroy中清空不必要的Message消息。

    2)可以将Handler放入到静态内部类中(静态内部类不会持有外部类的引用)。如果想要在handler内部去调用所在的外部类Activity,可以handler内部使用弱引用的方式指向所在Activity,这样不会导致内存泄漏。


    5.监听器导致内存泄漏

    系统服务可以通过context.getSystemService获取,它们负责执行某些后台任务,或者为硬件访问提供接口。如果 context对象想要在服务内部的事件发生时被通知,那就需要把自己注册到服务的监听器中。然而,这会让服务持有Activity的引用,如果忘记在Activity销毁时取消注册,那就会导致Activity泄漏了。

    void registerListener() {
           SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
           Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
           sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
    }
    
    View smButton = findViewById(R.id.sm_button);
    smButton.setOnClickListener(new View.OnClickListener() {
        @Override public void onClick(View v) {
            registerListener();
            nextActivity();
        }
    });
    


    下一篇将会从资源角度来说明内存泄漏的问题。


  • 相关阅读:
    codevs 2021 中庸之道
    bzoj 1227: [SDOI2009]虔诚的墓主人
    cogs 2620. [HEOI2012]朋友圈
    bzoj 3123: [Sdoi2013]森林(45分暴力)
    cogs 1685 魔法森林
    bzoj 1061: [Noi2008]志愿者招募
    poj 1743 Musical Theme
    bzoj 1001: [BeiJing2006]狼抓兔子
    bzoj 4006: [JLOI2015]管道连接
    hdu 5693 D Game
  • 原文地址:https://www.cnblogs.com/qitian1/p/6461545.html
Copyright © 2011-2022 走看看