在 Android 开发中,内存溢出(OOM)和内存泄漏(Memory Leak)是常见的内存管理问题,它们的表现和解决方案与 Android 系统的特性密切相关
一、内存溢出(OutOfMemoryError, OOM)
定义:
Android 应用在申请内存时,超出系统分配给该进程的最大内存限制(如堆内存上限),导致程序崩溃。
常见原因:
-
堆内存限制:不同设备堆内存上限不同(通常 64MB~512MB),超出会触发
OutOfMemoryError
。 -
大对象滥用:
-
未压缩的
Bitmap
占用内存过高(如加载高分辨率图片)。 -
频繁创建大型临时对象(如数组、集合)。
-
-
内存泄漏累积:内存泄漏长期未解决,最终可用内存不足。
-
资源未释放:如未关闭
Cursor
、未回收MediaPlayer
。
解决方案:
-
优化 Bitmap 处理:
-
使用
BitmapFactory.Options
的inSampleSize
压缩图片。 -
采用
Glide
或Picasso
等图片库自动管理内存。 -
使用
ARGB_4444
或RGB_565
降低色彩质量(牺牲画质换内存)。
-
-
避免大对象频繁创建:
-
使用对象池(如
RecyclerView
的 ViewPool)。 -
减少临时对象的创建(如字符串拼接改用
StringBuilder
)。
-
-
监控内存使用:
-
通过 Android Profiler 观察内存波动。
-
调用
ActivityManager.getMemoryClass()
获取当前设备的堆内存限制。
-
-
主动释放资源:
-
在
onDestroy()
中释放Bitmap
、关闭数据库连接。 -
实现
ComponentCallbacks2.onTrimMemory()
响应内存紧张事件。
-
二、 内存泄漏 (Memory Leak)
定义:
对象已经不再使用,但由于错误引用无法被垃圾回收(GC),导致内存被持续占用。
常见场景:
-
静态引用 Activity/View:
public class MyUtils { private static Activity sActivity; // 错误!静态变量持有 Activity 引用 }
-
未注销监听器/回调:
-
注册
BroadcastReceiver
、SensorManager
后未反注册。 -
Handler
或Runnable
持有 Activity 引用(如延迟任务未取消)。
-
-
匿名内部类隐式引用:
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 匿名内部类隐式持有外部 Activity 引用 } });
-
单例模式错误持有 Context:
public class Singleton { private static Singleton instance; private Context mContext; // 若传入 Activity Context 会导致泄漏 private Singleton(Context context) { mContext = context.getApplicationContext(); // 正确:使用 Application Context } }
解决方案
-
避免长生命周期对象引用短生命周期对象:
-
使用
WeakReference
或SoftReference
替代强引用。 -
单例模式中只保存
Application Context
(而非 Activity Context)。
-
-
及时注销监听器与回调:
@Override protected void onDestroy() { super.onDestroy(); sensorManager.unregisterListener(this); // 反注册 handler.removeCallbacksAndMessages(null); // 移除 Handler 消息 }
-
使用静态内部类 + WeakReference:
private static class MyHandler extends Handler { private WeakReference<Activity> mActivityRef; MyHandler(Activity activity) { mActivityRef = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { Activity activity = mActivityRef.get(); if (activity != null) { // 处理消息 } } }
-
工具检测与修复:
-
LeakCanary:自动检测内存泄漏并生成报告。
-
Android Profiler:分析堆转储(Heap Dump),查找未被回收的对象引用链。
-
三、两者的关键区别
特征 | 内存溢出(OOM) | 内存泄漏(Memory Leak) |
触发时机 | 内存不足时立即崩溃 | 内存逐渐被占用,可能最终导致 OOM |
直接原因 | 内存需求超出系统限制 | 对象引用未释放,GC 无法回收 |
调试优先级 | 需紧急优化内存占用 | 需长期监控和修复代码缺陷 |
四、案例
内存泄漏 → OOM
-
案例 1:Activity 被静态单例持有,导致 Activity 无法销毁,多次启动后 OOM。
-
案例 2:Handler 发送延迟消息后未清除,Activity 销毁后消息队列仍持有 Handler 引用。
直接 OOM
-
案例:在 ListView 中直接加载原图 Bitmap,快速滑动列表时频繁创建 Bitmap 触发 OOM