java垃圾回收器,开发者无需特意管理内存分配,降低了应用由于局部故障导致崩溃,同时防止未释放的内存把堆栈挤爆的可能,所以写出的代码更为安全。
但是,在java中仍存在很多容易导致内存泄漏的逻辑可能。如果不小心,则很容易浪费掉未释放的内存,最终导致内存用光的错误抛出OOM
内存泄漏
- 一般内存泄漏(traditional memory leak):由忘记释放分配的内存导致的(Cursor忘记关闭等)
- 逻辑内存泄漏(logical memory leak):当应用不再需要这个对象,但仍未释放该对象的所有引用
如果持有对象的强引用,垃圾回收器是无法在内存中回收这个对象
Android 中,最容易引发的内存泄漏为 Context,如Activity的Context,就包含了大量的内存引用,如View Hierarchies和其他资源。一旦泄漏Context,就意味着泄漏它指向的所有对象。android机器内存有限,太多的内存泄漏容易导致OOM
Activity.onDestroy()被视为Activity生命的结束,程序上看来,它应该被销毁,或者Android系统需要回收这些内存(当内存不够时,android会回收看不见的activity)
如果Activity.onDestroy()执行完,堆栈中仍存在持有该activity的引用,垃圾回收器就无法把他标记成已回收的内存(结果就是Activity存活在它的生命周期之外)
导致潜在的内存泄漏的陷阱不外乎两种:
- 全局进程(process-global)的static变量。这个无视应用状态,持有activity的强引用的东西
- 活在Activity生命周期之外的线程。没有清空对Activity的强引用。
----------------------------------------------------------------------------------------------------------------
Static Activity
在类中定义了静态Activity变量,把当前运行的activity实例赋值于这个静态变量。
如果这个静态变量在activity生命结束后没有清空,就会导致内存泄漏。因为static贯穿了这个应用的生命周期,所以被泄露的Activity就会一直存在于应用的进程中,不会被垃圾回收器回收。
避免这种代码 static Activity activity;
----------------------------------------------------------------------------------------------------------------
Static Views 单例中保存activity
在单例中,如果Activity经常被用到,那么内存中保存一个实例是很实用的。但由于单例的生命周期是应用程序的生命周期,这样做会延长Activity的生命周期(强制延长Activity的生命周期是相当危险且不必要的,无论如何都不能在单例中保存类似的Activity对象)
特殊情况:如果一个View初始化耗费大量资源,而且在一个activity生命周期内保持不变,那可以把它变成static,加载到视图树上(View Hierachy),像这样,当activity被销毁时,应当释放资源。(在销毁视图时应该把这个static view的view置为null)
在调用Singleton的getInstance()方法时传入了Activity,那么当instance 没有释放时,这个Activity会一直存在。因此造成内存泄漏。
可以在传入context时传入context.getApplicationContext()即可
static view也不建议,或者应该在销毁时置null
Inner Classes:提高可读性,但是导致内存泄漏的原因,就是内部持有外部类实例的强引用,如内部类中持有Activity对象
解决办法,1.将内部类变为静态内部类,把匿名内部类变为静态匿名内部类
2.如果有强引用Activity中的属性,则将该属性的引用方式改为弱引用
3.在业务运行的情况下,当Activity执行onDestroy时,结束这些耗时任务
----------------------------------------------------------------------------------------------------------------
Inner Classes
假设Activity中有个内部类,这样做可以提高可读性和封装性。假如我们创建一个内部类,而且持有一个静态变量的引用,那么很有可能内存泄漏(销毁的时候置空即可)
内部类的优势之一就是可以访问外部类,不好的是,导致内存泄漏的原因,就是因为内部类持有外部类实例的引用。
----------------------------------------------------------------------------------------------------------------
Anonymous Classes
匿名类也维护了外部类的引用。所以内存泄漏很容易发生,当你在Activity中定义了匿名的AsyncTask。当阴补任务在后台执行耗时任务期间,Activity不幸被销毁(用户退出,系统回收),这个被AsyncTask持有的Activity实例就不会被垃圾回收器回收,直到异步任务结束。
----------------------------------------------------------------------------------------------------------------
Handler
同样,定义匿名的Runnable,用匿名Handler执行。Runnable内部类会持有外部类的隐式引用,被传递到Handler的消息队列MessageQueue中,在Message消息没有被处理之前,Activity实例不会被销毁,于是导致了内存泄漏。
----------------------------------------------------------------------------------------------------------------
Threads
只要是匿名类的实例,不管是不是在工作线程,都会持有Activity的引用,导致内存泄漏
----------------------------------------------------------------------------------------------------------------
TimerTask
只要是匿名类的实例,不管是不是在工作线程,都会持有Activity的引用,导致内存泄漏
----------------------------------------------------------------------------------------------------------------
Sensor Manager
通过Context.getSystemService(int name) 可获取系统服务。这些服务工作在各自的进程中,帮助应用处理后台任务,处理硬件交互。如果需要使用这些服务,可注册监听器,这会导致服务持有了Context的引用,如果在Activity销毁的时候没有注销这些监听,会导致内存泄漏