每日一道面试题(第三期)---一般什么情况下会导致内存泄漏问题

栏目: Android · 发布时间: 5年前

内容简介:当本应该被释放或无用的对象,因为被其他存活的对象持有其引用,导致该对象不能被垃圾回收器回收,一直占用着内存,使程序运行变得缓慢甚至崩溃。为什么被其他存活的对象持有其引用,就不能被回收?这个就需要了解java的垃圾回收机制。什么样的对象会被认为需要回收呢?我们现在将每一个对象看作有向图的结点,而对象之间的引用关系则是有向图的边。那么一定会有一个起始结点对象,如果这个对象是

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

原因

为什么被其他存活的对象持有其引用,就不能被回收?这个就需要了解 java 的垃圾回收机制。

java垃圾回收机制

什么样的对象会被认为需要回收呢?我们现在将每一个对象看作有向图的结点,而对象之间的引用关系则是有向图的边。那么一定会有一个起始结点对象,如果这个对象是

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

那么由此对象可以在有向图上遍历到的所有对象都不会被回收。反之,就会被认为是要回收的对象。

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

所以,就是我们常说的生命周期不同的两个对象间有引用关系,生命周期短的可能会造成内存泄漏,持续的时间取决于生命周期长的对象。如果这个对象是静态变量,那么将会持续到整个程序运行结束。

Android内存泄漏情况

集合类

一般的集合类并不会造成内存泄漏,但是如果是全局性的集合类,如果不注意在使用完毕后进行remove操作,就极有可能造成内存泄露。

单例模式

这里的单例模式是指创建时需要传入Context作为参数。比如我们常写的下面这个代码。

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,造成内存压力。


以上所述就是小编给大家介绍的《每日一道面试题(第三期)---一般什么情况下会导致内存泄漏问题》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

奇点临近

奇点临近

Ray Kurzweil / 董振华、李庆成 / 机械工业出版社 / 2011-10 / 69.00元

人工智能作为21世纪科技发展的最新成就,深刻揭示了科技发展为人类社会带来的巨大影响。本书结合求解智能问题的数据结构以及实现的算法,把人工智能的应用程序应用于实际环境中,并从社会和哲学、心理学以及神经生理学角度对人工智能进行了独特的讨论。本书提供了一个崭新的视角,展示了以人工智能为代表的科技现象作为一种“奇点”思潮,揭示了其在世界范围内所产生的广泛影响。本书全书分为以下几大部分:第一部分人工智能,第......一起来看看 《奇点临近》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具