内存泄露:当一个对象使用完成后,该被GC回收内存时,由于另一个对象持有它的引用,导致不能被回收,造成内存泄露。(其实就是某个对象所占的内存不能被回收,也就是这块内存被GC“泄露”回收了,就是内存泄露)。

内存泄露在Android中指的是Activity等组件的泄露。

1.Handler

Handler一般用来做耗时任务,有可能会造成内存泄露。一般情况下,我们会创建一个内部类handler(非静态的)去使用。**在java中,非静态内部类或匿名内部类会隐式的持有外部类的引用,**在这里就是Handler默认持有外部类Activity的引用。此时如果关闭Activity,handler中有未发送或未处理完的消息,则message就会持有handler的引用(源码中handler在发送消息时已经将自身与message绑定在一起了,msg.target就是handler,大家可以自行查看一下),而handler又持有activity的引用,导致activity来不及释放(内存不能被系统回收),造成内存泄露。

解决方案:

①创建静态内部类handler(静态内部类不会持有外部类的引用,但也不能调用外部的方法),不过可以在handler内部弱引用化Activity。

android 内存泄露 内部类 android常见的内存泄露_非静态

弱引用:一方面可以继续持有外部类的引用,即可以调用外部的方法;另一方面GC(垃圾回收器)一旦发现有弱引用对象的存在,不管内存空间是否充足,都会进行垃圾回收。这样“静态+弱引用”可以有效避免内存泄露了。

②将handler创建一个单独的类。(就是创建一个class继承自Handler即可,这里不再展示)。

2.非静态内部类创建的静态实例

非静态内部类或匿名内部类默认持有外部类的引用。如果非静态内部类创建了一个静态实例(或是一个静态变量引用了非静态内部类),静态(static),它的生命周期是和Application的生命周期是保持一致的。若Activity被finish(),静态实例会持有非静态内部类的引用,非静态内部类又持有Activity的引用,因为静态实例的生命周期过长,导致Activity及时销毁时不能被GC回收,造成内存泄露。

解决方案:将非静态内部类改为静态内部类即可。

3.单例(使用Activity的context)

一般情况下,我们创建一些manager的时候会使用到单例模式,它的目的是只提供一个实例供外部访问,保证全局的唯一性;减少对象创建的同时减少内存开支和系统性能开销等。创建单例的核心步骤:1.构造方法私有化;2.创建静态实例对象;3.创建静态方法。在创建静态方法时会传入Context,是为了获取更多的资源、完成和其它组件或服务的交互等。当我们使用单例的时候,如果传入的是Activity的Context,由于Activity的生命周期较短,随时可以销毁等;而单例中的对象生命周期较长,很容易持有Activity的引用,导致Activity不能被系统回收,造成内存泄露。

解决方案:context使用时注意生命周期,尽量传入Application的Context。

4.资源未关闭(File流、Cursor等)

由于File、Cursor等资源文件在使用时大多都用了缓冲,如果不关闭它们,缓冲所占内存不能被回收,很容易造成内存泄露。有时我们会将它们置为null,而不关闭它们,往往会造成内存泄露。

解决方案:在不使用它们的时候,应该及时关闭它们,即调用它的close()函数,以便它们的缓冲及时的被系统回收内存。

android 内存泄露 内部类 android常见的内存泄露_内部类_02


5.创建的listener或监听(BroadcastReceiver、EventBus等)

在Activity中,我们在使用时注册BroadcastReceiver,而如果没在Activity生命周期内调用unRegeisterReceiver()的话,由于BroadcastReceiver不止被Activity引用,还可能会被系统服务等引用,导致BroadcastReceiver无法被回收,而它仍然持有Activity的引用,导致Activity无法被回收,引起内存泄露。

解决方案:注册过的记得在Activity的onDestory()中取消注册(注销)。

android 内存泄露 内部类 android常见的内存泄露_内存泄露_03


6.线程(AsyncTask等)

AsyncTask是一种异步处理数据的异步类,其中有专门做耗时操作的方法doInBackground()和用来更新UI的方法onPostExecute()。我们在Activity中创建内部类AsyncTask,感觉它的生命周期和activity的生命周期是一致的,其实并不是,AnyncTask会一直执行,直到doInBackground方法执行完成。此时执行cancel操作,它占用的内存会通知进行回收。如果没有执行cancel操作或有时候即使执行了cancel操作,doInBackground()中也有可能存在仍然在执行的任务,如果Activity已经被销毁,仍然执行的任务继续保留AsyncTask的引用,而非静态内部类AsyncTask持有外部类Activity的引用,导致Activity无法被回收,造成内存泄露。

解决方案:使用静态AsyncTask,如果需要用到外部方法的话,在内部添加弱引用化的当前类Activity。

这是自己的分析,有不对的地方还望指出。每天学习一点点。