美文网首页Android面试题
【知识】内存泄露

【知识】内存泄露

作者: FynnJason | 来源:发表于2019-12-26 11:05 被阅读0次

原创不易,尊重作者,转载请注明出处

前言

内存泄露是指当一个对象已经不需要再使用本该回收时候,另外一个正在使用的对象持有它的引用从而导致它不能被回收,该对象就会停留在堆内存中,这就产生了内存泄露。

内存泄露的影响

内存泄露是造成App出现OOM的主要原因。

检测内存泄露的方法

借助两个工具:MATLeakCanary

MAT:强大的内存分析工具。

LeakCanary:Square开源的一款轻量级的第三方内存泄漏检测工。

常见的内存泄露及解决办法

1、单例造成的内存泄露

由于单例的静态特性使得其生命周期和应用的生命周期一样长,如果一个对象已经不再需要使用了,而单例对象还持有该对象的引用,那么就会使得该对象不能被正常回收,从而导致内存泄露。

典型例子:

public class SingleInstanceTest {

    private static SingleInstanceTest sInstance;
    private Context mContext;

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

    public static SingleInstanceTest newInstance(Context context){
        if(sInstance == null){
            sInstance = new SingleInstanceTest(context);
        }
        return sInstance;
    }
}

由于需要传入一个Context,所以这个Context的生命周期的长短至关重要:

1).如果此时传入的是 Application 的 Context,因为 Application
的生命周期就是整个应用的生命周期,所以这将没有任何问题。

2).如果此时传入的是 Activity 的 Context,当这个 Context 所对应的 Activity 退出时,由于该
Context 的引用被单例对象所持有,其生命周期等于整个应用程序的生命周期,所以当前 Activity
退出时它的内存并不会被回收,这就造成了内存泄漏。

解决办法

⑴获取Application的Context

public class SingleInstanceTest {

    private static SingleInstanceTest sInstance;
    private Context mContext;

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

    public static SingleInstanceTest newInstance(Context context){
        if(sInstance == null){
            sInstance = new SingleInstanceTest(context);
        }
        return sInstance;
    }
}

⑵使用弱引用的方式

public class Sample {

    private WeakReference<Context> mWeakReference;

    public Sample(Context context){
        this.mWeakReference = new WeakReference<>(context);
    }

    public Context getContext() {
        if(mWeakReference.get() != null){
            return mWeakReference.get();
        }
        return null;
    }
}

被弱引用关联的对象只能存活到下一次垃圾回收之前,也就是说即使 Sample 持有 Activity 的引用,但由于 GC 会帮我们回收相关的引用,被销毁的 Activity 也会被回收内存,这样我们就不用担心会发生内存泄露了。

2、非静态内部类创建静态实例造成的内存泄漏

对比 静态内部类 非静态内部类
与外部 class 引用关系 如果没有传入参数,就没有引用关系 自动获得强引用
被调用时需要外部实例 不需要 需要
能否调用外部 class 中的变量和方法 不能
生命周期 自主的生命周期 依赖于外部类,甚至比外部类更长

从上表可以看出,非静态内部类自动获取外部类的强引用,而它的生命甚至比外部类更长,如果一个activity的非静态内部类的生命周期比activity更长,那么activity的内存便无法被回收,还有可能发生空指针问题。

public class MainActivity extends AppCompatActivity {

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

    class MyAscnyTask extends AsyncTask<Void, Integer, String>{
        @Override
        protected String doInBackground(Void... params) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "";
        }
    }
}

我们在activity中继承AsyncTask自定义了一个非静态内部类,在doInbackground方法中做了耗时操作,然后在onCreate中启动MyAsyncTask,如果在耗时操作结束前,activity被销毁了,这时候因为MyAsyncTask持有activity的强引用,便会导致activity的内存无法被回收。

解决办法
将非静态内部类变成静态内部类

public class MainActivity extends AppCompatActivity {

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

    static class MyAscnyTask extends AsyncTask<Void, Integer, String>{
        @Override
        protected String doInBackground(Void... params) {
            try {
                Thread.sleep(50000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "";
        }
    }
}

3、匿名类造成的内存泄露

匿名类和非静态内部类有相同的共同点是持有外部类的引用,匿名类造成内存泄露的原因也跟非静态内部类基本一样。

public class MainActivity extends AppCompatActivity {

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // ① 匿名线程持有 Activity 的引用,进行耗时操作
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(50000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        // ② 使用匿名 Handler 发送耗时消息
        Message message = Message.obtain();
        mHandler.sendMessageDelayed(message, 60000);
    }

上面代码中有两处可能产生内存泄露:
1).匿名的thread进行耗时操作时,如果MainActivity被销毁而thread中的耗时操作没有结束的话,便会产生内存泄露。

2).匿名的handler发送消息时,如果MainActivity被销毁,而handler里面的消息还有没有发送完毕时,activity的内存也不会被回收。

解决办法
1).继承thread实现静态内部类

2).继承handler实现静态内部类,在activity销毁时,移除所有消息mHandler.removeCallbacksAndMessages(null);

4、资源未关闭造成的内存泄露

在使用broadcast、file、cursor、stream、bitmap等资源后,没有在activity销毁时关闭或注销,那么资源就不会被回收,从而造成内存泄露。

解决办法

在activity销毁时关闭或注销资源。

5、集合类操作的内存泄露

集合类添加元素后,仍引用着集合元素对象,导致该集合中的元素对象无法被回收,从而导致内存泄露。

   static List<Object> objectList = new ArrayList<>();
   for (int i = 0; i < 10; i++) {
       Object obj = new Object();
       objectList.add(obj);
       obj = null;
    }

在这个例子中,object对象放入静态集合中,因为静态变量的生命周期和应用程序一致,所以他们所引用的对象object也不能释放,这样便造成了内存泄露。

解决办法

在集合元素使用后从集合中删除,等所有元素都使用完之后,将集合置空。

    objectList.clear();
    objectList = null;

6、WebView造成的内存泄露

webview一旦使用,就会造成内存泄露

解决办法

开启一个进程,通过AIDL与主线程通信,在合适的时机销毁。

相关文章

  • Android 内存泄漏分析

    一、基础知识 1.1、内存泄露、内存溢出: 内存泄露(Memory Leak)指一个无用对象持续占有内存或无用对象...

  • 【知识】内存泄露

    原创不易,尊重作者,转载请注明出处 前言 内存泄露是指当一个对象已经不需要再使用本该回收时候,另外一个正在使用的对...

  • 内存泄露系列文章(一) - 内存泄露原因及影响

    前言 内存泄露系列文章内存泄露系列文章(一) - 内存泄露原因及影响内存泄露系列文章(二) - 内存泄露监测及分析...

  • 内存泄露系列文章(三) - 内存泄露解决方案

    前言 内存泄露系列文章内存泄露系列文章(一) - 内存泄露原因及影响内存泄露系列文章(二) - 内存泄露监测及分析...

  • Android常见内存泄露分析总结

    一、基础知识 1、什么是内存泄露 java中的内存泄露是指一个无用对象持续占有内存或无用对象的内存得不到及时的释放...

  • 内存溢出与内存泄露

    目录 [TOC] 1 内存泄露与内存溢出的区别 1.1 内存泄露 内存泄露(Memory Leak),指的是堆内存...

  • 内存泄漏详细分析

    首先,什么是内存泄露?经常听人谈起内存泄露,但要问什么是内存泄露,没几个说得清楚。内存泄露是指无用对象(不再使用的...

  • android内存泄露

    参考内存泄露从入门到精通三部曲之基础知识篇Android 内存泄漏总结Android内存泄漏研究Android内存...

  • Java弱引用学习 WeakHashMap、ReferenceQ

    上一篇文章 Java内存泄露学习 ThreadLocal真的会内存泄露吗 提到ThreadLocal内存泄露的问题...

  • 内存优化

    内存优化主要是分析内存泄露和内存溢出。将从内存是怎么分配,内存怎么出现泄露和溢出,用工具判断什么情况出现泄露,找出...

网友评论

    本文标题:【知识】内存泄露

    本文链接:https://www.haomeiwen.com/subject/iuekoctx.html