Android 内存泄露记录
当一个不再使用的对象被其他长生命周期对象持有引用时,造成该对象在系统触发GC时无法回收,以致该对象在堆中所占的内存空间无法释放,造成内存空间浪费,此现象即为内存泄漏
常见的几种内存泄漏
1. Context引起内存泄漏
- 延申:长生命周期的对象不要强引用Activity或Fragment对象
原因
- 短生命周期对象持有长生命周期的对象,回收时,因其被持有所以无法回收
修复方案
- 弱引用context对象 或 使用Application的context对象
class XxxActivity: AppCompatActivity() {
private lateinit var mInstance: XxxInstance
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 方式一:此种方式当activity销毁时不存在内存泄露
// 原因:XxxActivity与XxxInstance双相依赖,属于属于双向可达状态
// 并且除了这里两个类外无其他三方类引用这两个类,此时若activity回收后GC时会同时回收两者
mInstance = XxxInstance(this)
// 方式二:单例模式,当activity销毁时会引起内存泄露
// 原因:单例模式,单例对象生命周期等同于应用生命周期
// XxxActivity销毁时,因单例中还在引用,所以无法被回收
// 解决:单例中使用弱引用可规避其引起的内存泄露问题
mInstance = XxxInstance(this)
}
}
// 方式一
class XxxInstance(private val context: Context) {
}
// 方式二:单例模式
class XxxInstance private constructor(private val context: Context) {
companion object {
@SuppressLint("StaticFieldLeak")
@Volatile private var instance: XxxInstance? = null
fun getInstance(context: Context) =
instance ?: synchronized(this) {
instance ?:XxxInstance(context).also { instance = it }
}
}
}
2. 非静态内部类持有外部类引用
原因
- 非静态内部类默认持有外部类引用,若在外部类中创建静态内部类对象,则在外部类销毁时,因被内部类所引用,所以无法回收,最终引起内存泄露
修复方案
- 定义为静态内部类(若内部显示引用外部类成员需通过弱引用实现)
/**
* 解决方案:将Test改为静态内部类(static class Text)
*/
class XActivity extends AppCompatActivity {
private static Test test = null;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_x);
if (test == null) {
test = new Test();
}
// ...
}
// kotlin中使用inner即为内部类,会有外部类对象的引用
// kotlin中不使用inner即为嵌套类,不持有外部类对象,等同于Java中的静态内部类
class Test {
// ...
}
}
3. Handler使用不当引起内存泄漏
原因:
-
- 非静态内部类Handelr 或 匿名内部类Handelr
-
- Activity被销毁时Handelr消息未处理完
修复方案
-
- Handler改为静态内部类,内部弱引用Activity对象
-
- Activity#onDestroy()中移除队列中消息
/**
* 程序启动时,主线程会创建Looper对象,内部维护一个MessageQueue消息队列,按时间顺序存放Message
*
* 1. 若Handler通过内部类创建,内部类持有外部类引用(Activity),而队列中的消息target指向Handler,即消息持有Handler引用
* 2. 若Activity销毁时队列中的消息还未处理完,这些未处理完的消息会持有Activity引用,导致Activity无法回收
*
* 解决方案:
* 1. Handler改为静态内部类,Handler内弱引用Activity对象,Activity销毁时让内部类不再持有外部类的引用
* 2. Activity#onDestroy()中移除队列中消息,
*/
class XxxActivity extends AppCompatActivity {
XxxHandler mHandler = new XxxHandler(this);
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new Thread(new Runnable() {
@Override
public void run() {
mHandler.sendMessage(Message.obtain());
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
// 移除对应的Runnable或者是Message
// mHandler.removeCallbacks(runnable);
// mHandler.removeMessages(what);
mHandler.removeCallbacksAndMessages(null);
}
private static class XxxHandler extends Handler {
private WeakReference<Activity> mActivity;
public XxxHandler(Activity activity) {
mActivity = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
if (mActivity.get() == null || mActivity.isFinishing()) {
return;
}
// to do something..
}
}
}
4. 资源未回收引起的内存泄露
- 广播注册但未取消注册
- Bitmap使用后未回收
- 服务开启或绑定但未关闭或解绑
- 数据库cursor使用后未关闭
- 网络请求页面关闭时未取消(例:图片加载未绑定view生命周期等)
- 文件流使用后未关闭
- 多媒体(音视频)使用后未释放
- Socket使用后未关闭
- 三方库注册后未取消注册(例:EventBus)
- 为便于Activity管理,将其添加至栈中,销毁时未移除
解决方案:
- kotlin中可使用use函数管理资源确保使用后自动关闭(例文件、流等)
其他内存泄露
1. WebView引起内存泄漏
两种优化方案:
- 为WebView开辟一个独立进程,操作结束后结束进程(System.exit(0));与其通信可采用AIDL、Messager、ContentProvider、Broadcast、Intent等方式
- xml中定义一个容器类view(例FrameLayout(layout)),在代码中动态添加WebView(context可使用弱引用),onDestroy()销毁时直接layout.removeAllViews(),移除WebView
2. 线程使用不当造成的内存泄漏
原因:
- Activity销毁时,若任务未执行完毕,会导致Activity无法被回收
1. Asynctask
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(8000);
return null;
}
}.execute();
2. Thread
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(8000);
}
}).start();
解决方案:
- 静态内部类,Activity销毁时取消任务
1. AsyncTask
static class XAstncTask extends AsyncTask<Void, Void, Void> {
private WeakReference<XActivity> reference;
public XAstncTask(XActivity activity) {
reference = new WeakReference(activity);
}
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(8000);
return null;
}
@Override
protected Void onPostExecute(Void void) {
super.onPostExecute(void)
val activity = (XActivity) reference.get();
if (activity == null || activity.isDestroy() || activity.isFinishing()) {
return;
}
...
}
}
2. Thread
static class XRunnable implements Runnable {
@Override
public void run() {
SystemClock.sleep(8000);
}
}
// 使用
XAsyncTask task = new XAsyncTask(this);
task.execute();
new Thread(new XRunnable()).start();
@Override
protected void onDestroy() {
super.onDestroy();
// 取消任务
task.cancel();
}