内存优化实战
场景
App 在低端设备上频繁 OOM 崩溃,Java Heap 使用长期逼近 maxMemory 上限。
排查与方案
1. 定位内存问题
# 查看进程内存概况
adb shell dumpsys meminfo com.example.app
使用 Android Studio Profiler 的 Memory 面板:
- Allocation Tracking — 定位哪些代码在频繁创建对象
- Heap Dump — 分析当前堆中各类对象的数量和大小
- LeakCanary — 自动检测 Activity/Fragment 内存泄漏
2. 常见问题与修复
| 问题 | 修复 |
|---|---|
| Activity 泄漏 | 静态引用、非静态内部类持有 Activity → 改为弱引用或静态内部类 |
| Bitmap 大量缓存 | 无上限缓存 → LRU 策略 + inBitmap 复用 |
| 集合只增不减 | 全局 List/Map 持续增长 → 及时 remove + 弱引用 |
| 注册未反注册 | EventBus / BroadcastReceiver → onDestroy 中注销 |
| WebView 泄漏 | WebView 在独立进程中运行,退出时杀进程 |
// ❌ 经典泄漏:非静态内部类持有外部 Activity 引用
class MainActivity : AppCompatActivity() {
private val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
// 这个匿名内部类隐式持有 MainActivity 的引用
updateUI()
}
}
}
// ✅ 修复:静态内部类 + WeakReference
class MainActivity : AppCompatActivity() {
private val handler = SafeHandler(this)
private class SafeHandler(activity: MainActivity) : Handler(Looper.getMainLooper()) {
private val ref = WeakReference(activity)
override fun handleMessage(msg: Message) {
ref.get()?.updateUI() // Activity 已销毁则不执行
}
}
}
3. 大图优化
// 按目标尺寸采样加载,避免将 4000x3000 的原图全加载到内存
fun decodeSampledBitmap(res: Resources, resId: Int, reqWidth: Int, reqHeight: Int): Bitmap {
val options = BitmapFactory.Options().apply {
inJustDecodeBounds = true // 只读尺寸不加载像素
}
BitmapFactory.decodeResource(res, resId, options)
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
options.inJustDecodeBounds = false
return BitmapFactory.decodeResource(res, resId, options)
}
低端设备策略
对 Runtime.maxMemory() < 256MB 的设备主动降级:
- 图片缓存上限减半
- 关闭列表预加载
- 降低图片分辨率
面试答题要点
- 先用工具定位(Profiler Heap Dump + LeakCanary)
- 按泄漏类型分类修复
- Bitmap 优化(采样、复用、LRU)是高频考点
- 提到低端设备的自适应降级策略