内存泄漏的常见场景与解决方案
一、内存泄漏的本质
内存泄漏是指程序在运行过程中,某些对象不再被使用,但由于被其他对象引用,导致垃圾回收机制无法回收它们,从而造成内存浪费。
二、常见的内存泄漏情况
1. 单例模式引发的泄漏
单例对象的生命周期通常与应用一致。如果单例持有了某个短生命周期对象的引用,例如 Activity,就会导致该对象无法被回收。
public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context.getApplicationContext(); // 使用Application上下文
}
public static AppManager getInstance(Context context) {
if (instance == null) {
instance = new AppManager(context);
}
return instance;
}
}
避免使用 Activity 上下文创建单例对象,应使用 Application 的上下文。
2. 静态变量导致的泄漏
静态变量的生命周期与应用相同。如果静态变量引用了某个对象,而该对象不再需要,但又无法被回收,就会造成内存泄漏。
public class MainActivity extends AppCompatActivity {
private static UserInfo info;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (info == null) {
info = new UserInfo(this); // 持有Activity引用
}
}
}
class UserInfo {
public UserInfo(Activity activity) {
}
}
建议在对象不再使用时手动将静态变量置为 null。
3. 非静态内部类导致的泄漏
非静态内部类默认持有外部类的引用。如果内部类生命周期过长,就会导致外部类无法被回收。
例如使用 Handler 时:
public class MainActivity extends AppCompatActivity {
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new MyHandler(this);
handler.sendEmptyMessage(1);
}
private static class MyHandler extends Handler {
private WeakReference<MainActivity> activityReference;
public MyHandler(MainActivity activity) {
activityReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = activityReference.get();
if (activity != null) {
// 处理逻辑
}
}
}
@Override
protected void onDestroy() {
handler.removeCallbacksAndMessages(null);
super.onDestroy();
}
}
使用静态内部类配合弱引用,可以有效避免内存泄漏。
4. 未注销监听器或回调
注册广播接收器、观察者等后,如果未及时注销,也会造成内存泄漏。
public class MainActivity extends AppCompatActivity {
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 接收广播逻辑
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
registerReceiver(receiver, new IntentFilter("ACTION"));
}
@Override
protected void onDestroy() {
unregisterReceiver(receiver);
super.onDestroy();
}
}
5. Timer 和 TimerTask 引发的泄漏
如果在 Activity 销毁时未停止 Timer,它将继续运行并持有 Activity 引用。
@Override
protected void onDestroy() {
if (timer != null) {
timer.cancel();
timer = null;
}
if (timerTask != null) {
timerTask.cancel();
timerTask = null;
}
super.onDestroy();
}
6. 集合类持有对象未释放
集合类如 ArrayList、HashMap 等若长期持有无用对象,也会造成内存泄漏。应适时移除无用对象。
7. 未关闭资源文件或数据库连接
使用完 IO、数据库游标等资源后应及时关闭,否则缓冲区对象无法释放。
8. 属性动画未取消
属性动画如 ObjectAnimator 在 Activity 销毁时未取消,会继续引用控件和 Activity。
@Override
protected void onDestroy() {
animator.cancel();
super.onDestroy();
}
9. WebView 导致的泄漏
WebView 在加载网页后会占用大量内存,销毁时需特别处理。
@Override
protected void onDestroy() {
if (webView != null) {
((ViewGroup) webView.getParent()).removeView(webView);
webView.stopLoading();
webView.getSettings().setJavaScriptEnabled(false);
webView.clearHistory();
webView.removeAllViews();
webView.destroy();
}
super.onDestroy();
}
三、优化策略
- 单例对象避免使用 Activity 上下文
- 静态引用应谨慎使用,及时置空
- 使用静态内部类 + 弱引用替代非静态内部类
- 及时注销广播和观察者
- Activity 销毁时取消耗时任务和动画
- 及时关闭文件流和数据库连接
- WebView 销毁前从父容器中移除