内存优化策略
问题
Android 应用有哪些系统化的内存优化策略?
答案
整体优化策略
1. 减少内存分配
// ❌ 循环中创建大量临时对象
fun formatList(items: List<Item>): List<String> {
return items.map { "${it.name} - ${it.price}" } // 每次 map 创建 String
}
// ✅ StringBuilder 复用
fun formatList(items: List<Item>): List<String> {
val sb = StringBuilder()
return items.map { item ->
sb.clear()
sb.append(item.name).append(" - ").append(item.price)
sb.toString()
}
}
// ✅ 使用 SparseArray 替代 HashMap<Int, V>(减少自动装箱)
val map = SparseArray<User>()
// 而非 HashMap<Int, User>()
2. 响应系统内存警告
class MyApp : Application(), ComponentCallbacks2 {
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
when (level) {
TRIM_MEMORY_UI_HIDDEN -> {
// 应用进入后台,释放 UI 相关资源
clearImageCache()
}
TRIM_MEMORY_RUNNING_LOW,
TRIM_MEMORY_RUNNING_CRITICAL -> {
// 系统内存紧张
clearAllCaches()
}
TRIM_MEMORY_COMPLETE -> {
// 系统即将杀死后台进程
releaseEverything()
}
}
}
}
3. LruCache 控制缓存
// 基于可用内存的 1/8 作为缓存大小
val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()
val cacheSize = maxMemory / 8
val bitmapCache = object : LruCache<String, Bitmap>(cacheSize) {
override fun sizeOf(key: String, bitmap: Bitmap): Int {
return bitmap.byteCount / 1024
}
override fun entryRemoved(evicted: Boolean, key: String, oldValue: Bitmap, newValue: Bitmap?) {
// 可以将被淘汰的 Bitmap 加入复用池
}
}
4. 使用 @JvmStatic 和 object 减少对象分配
// ✅ companion object 中的常量不会创建额外对象
class Constants {
companion object {
@JvmStatic val MAX_RETRY = 3
}
}
优化效果验证
# 前后对比内存
adb shell dumpsys meminfo com.example.app
# 或使用 Macrobenchmark
@Test
fun memoryBenchmark() = macrobenchmarkRule.measureRepeated(
metrics = listOf(MemoryUsageMetric(MemoryUsageMetric.Mode.Max)),
iterations = 5
) {
startActivityAndWait()
// 操作场景
}
常见面试问题
Q1: ArrayMap / SparseArray 和 HashMap 的区别?
答案:
| 特性 | HashMap | ArrayMap / SparseArray |
|---|---|---|
| 数据结构 | 数组 + 链表/红黑树 | 两个数组 |
| 内存占用 | 大(Entry 对象开销) | 小 |
| 查找速度 | O(1) | O(log n)(二分查找) |
| 适用场景 | 大量数据 | 千条以内的小数据 |
Android 官方建议在数据量 < 1000 时使用 ArrayMap/SparseArray。
Q2: 如何检测内存抖动?
答案:
内存抖动是指短时间内大量分配并回收对象,导致频繁 GC。
检测方法:
- Memory Profiler 中观察锯齿状内存曲线
- 使用 Allocation Tracking 定位高频分配点
- Systrace/Perfetto 中检查 GC 频率
常见原因:循环中创建对象、onDraw() 中分配对象。