跳到主要内容

内存优化实战

场景

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 的设备主动降级:

  • 图片缓存上限减半
  • 关闭列表预加载
  • 降低图片分辨率

面试答题要点

  1. 先用工具定位(Profiler Heap Dump + LeakCanary)
  2. 按泄漏类型分类修复
  3. Bitmap 优化(采样、复用、LRU)是高频考点
  4. 提到低端设备的自适应降级策略

相关链接