每日一道面试题(第3期)---通常什么状况下会致使内存泄漏问题

零零碎碎的东西老是记不长久,仅仅学习别人的文章也只是他人咀嚼后留下的残渣。无心中发现了这个每日一道面试题,想了想若是只是简单地去思考,那么不只会收效甚微,甚至难一点的题目本身可能都懒得去想,坚持不下来。因此不如把每一次的思考、理解以及别人的看法记录下来。不只加深本身的理解,更要激励本身坚持下去。java

内存泄漏

定义

当本应该被释放或无用的对象,由于被其余存活的对象持有其引用,致使该对象不能被垃圾回收器回收,一直占用着内存,使程序运行变得缓慢甚至崩溃。git

缘由

为何被其余存活的对象持有其引用,就不能被回收?这个就须要了解java的垃圾回收机制。github

java垃圾回收机制面试

什么样的对象会被认为须要回收呢?咱们如今将每个对象看做有向图的结点,而对象之间的引用关系则是有向图的边。那么必定会有一个起始结点对象,若是这个对象是框架

  • 方法区的类静态属性引用的对象
  • 方法区中的常量引用的对象
  • 本地方法栈中的native方法引用的对象
  • 虚拟机栈(栈帧中的本地变量表(局部变量表))所引用的对象

那么由此对象能够在有向图上遍历到的全部对象都不会被回收。反之,就会被认为是要回收的对象。ide

抽象的来讲,一个程序中会存在许多这样的有向图,若是一个对象同时被两个存在起始结点对象的有向图所引用。当一个有向图完成使命,须要被销毁,但另外一个有向图的生命周期尚未结束。那么这个本应该无用的对象,却不能被垃圾回收器回收,只有当另外一个有向图生命周期结束,才会被回收。post

因此,就是咱们常说的生命周期不一样的两个对象间有引用关系,生命周期短的可能会形成内存泄漏,持续的时间取决于生命周期长的对象。若是这个对象是静态变量,那么将会持续到整个程序运行结束。学习

Android内存泄漏状况

集合类

通常的集合类并不会形成内存泄漏,可是若是是全局性的集合类,若是不注意在使用完毕后进行remove操做,就极有可能形成内存泄露。this

单例模式

这里的单例模式是指建立时须要传入Context做为参数。好比咱们常写的下面这个代码。spa

public class Manager {
    private static Manager instance;
    private Context context;
    private Manager(Context context){
        this.context = context;
    }

    public static Manager getInstance(Context context){
        if(instance == null){
            instance = new Manager(context);
        }
        return instance;
    }
}
复制代码

关键就在于这个Context,若是这个Context是Activity的Content,那么显然Activity的生命周期和单例模式的对象的生命周期是不同的,传入Content的Activity使用完毕须要被回收时,是没法被垃圾回收器回收的。

显而易见的,当这个Context是Application的时,就不存在内存泄漏的问题。由于单例模式的对象与Application的生命周期都是整个应用的生命周期,不会有任何问题。

因此,咱们能够改成这样写

public class Manager {
    private static Manager instance;
    private Context context;
    private Manager(Context context){
        this.context = context.getApplicationContext();
    }

    public static Manager getInstance(Context context){
        if(instance == null){
            instance = new Manager(context);
        }
        return instance;
    }
}
复制代码

固然了,Application的Context也不是能随便用的。若是是要启动一个Activity,Application须要建立一个新的Task任务栈。而若是是建立一个Dialog,则只有Activity的context才能够。

匿名内部类

对于匿名内部类,在Android中典型的例子就是Handler了吧。这个我在第一期---自定义Handler如何有效保证内存泄漏问题已经说得很明白了。主要就是匿名内部类持有外部类的引用,匿名内部类的一些操做使得该内部类对象的生命周期和外部类的生命周期不相同,形成内存泄漏。

非静态内部类

在开发中,咱们为了程序的高效以及资源重复利用,咱们可能会常常写出这样的代码。

public class MainActivity extends BaseActivity {
    private static Resource resource = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(resource == null){
            resource = new Resource();
        }
    }

    class Resource{
    }
}
复制代码

这样作虽然有效的避免了资源的重复建立,每次在Activity启动时快速的使用这些资源,但却会形成内存泄漏。由于非静态内部类也默认会持有外部类的引用。而因为这个非静态内部类的静态实例,其生命周期会和整个应用程序同样长,因此会形成内存泄露。

解决办法就是将该内部类设为静态内部类,或者把这个内部类抽取出来封装成一个单例模式。

资源未关闭

在咱们使用BroadcastReceiver、File、Course、Stream、ContentObserver等资源或者一些框架eventbus等明确表示须要Register与unRegister时,都应该在Activity被销毁时关闭或者注销,不然这些资源将不会被回收。

不良代码形成的压力

有时也并非不能及时回收的对象形成的内存泄漏,而是有些代码没有及时有效的释放不须要使用的内存,或者是没有对于现有资源没有有效利用而频繁的申请新的内存,形成内存的巨大压力。

好比ListView中的ContentView,不使用ViewHolder有效的复用View而频繁的建立新的View,形成内存压力。

相关文章
相关标签/搜索