Skip to content

Android 内存管理深度指南

本章节涵盖: Android 内存模型、ART 虚拟机内存管理、LruCache、MemoryCache、Bitmap 内存管理、内存泄漏检测、内存优化技巧及面试考点。建议配合实际项目练习,深入理解内存管理的底层原理。


一、Android 内存模型

1.1 内存区域划分

Android 基于 Linux 内核,其内存管理继承并扩展了 Linux 的虚拟内存机制。理解 Android 内存区域划分是掌握内存管理的基础。

1.1.1 物理内存与虚拟内存

物理内存(Physical Memory):设备实际的 RAM 芯片,是真正的硬件资源。

虚拟内存(Virtual Memory):操作系统为每个进程提供的独立地址空间,进程看到的地址是虚拟地址,通过页表映射到物理地址。

Android 进程虚拟地址空间示意图(64 位系统):

┌─────────────────────────────────────────────┐
│  高地址区域                                │
├─────────────────────────────────────────────┤
│ 内核空间 (Kernel Space)                    │ ← 不可访问
│ - 内核代码                                 │
│ - 内核数据结构                             │
│ - 设备映射                                 │
├─────────────────────────────────────────────┤
│ 用户空间 (User Space)                      │
├─────────────────────────────────────────────┤
│ │ 堆 (Heap)                               │ ← 动态分配
│ │ - Java 对象                             │
│ │ - Native 堆内存                         │
├─────────────────────────────────────────────┤
│ │ 共享库 (Shared Libraries)              │
│ │ - ART 运行时库                          │
│ │ - 系统库                                │
├─────────────────────────────────────────────┤
│ │ 数据段 (Data Segment)                  │
│ │ - 全局变量                              │
│ │ - 静态变量                              │
├─────────────────────────────────────────────┤
│ │ 代码段 (Code Segment / Text)           │
│ │ - 可执行代码                            │
├─────────────────────────────────────────────┤
│ │ 栈 (Stack)                             │ ← 线程私有
│ │ - 局部变量                              │
│ │ - 方法调用栈帧                          │
│ │ - 寄存器上下文                          │
├─────────────────────────────────────────────┤
│  低地址区域                                │
└─────────────────────────────────────────────┘

1.1.2 关键内存区域详解

堆内存(Heap)

堆是 Java 对象和 Native 对象的主要存放区域,由垃圾回收器(GC)管理。

kotlin
// Java 堆内存示例
class User {
    var name: String? = null
    var email: String? = null
    var friends: List<User> = mutableListOf()
}

// 创建对象时,User 实例分配在堆上
val user = User()
user.name = "Alice"  // String 对象也在堆上

栈内存(Stack)

每个线程都有独立的栈,存储局部变量和方法调用信息。

kotlin
fun calculate(data: List<Int>): Int {
    var sum = 0  // 局部变量在栈上
    for (item in data) {
        sum += item  // 循环变量在栈上
    }
    return sum
}

// 调用栈帧结构:
// +------------------+
// | 返回地址         |
// | 参数 data        |
// | 局部变量 sum     |
// | 局部变量 item    |
// +------------------+

共享库(Shared Libraries)

存放系统库和应用的 Native 库,被多个进程共享。

共享库示例:
- libart.so (ART 虚拟机库)
- libc.so (C 标准库)
- libdvm.so (Dalvik 虚拟机库 - 旧版本)
- libsqlite3.so (SQLite 数据库)
- 应用的 .so 文件

1.2 Android 内存限制

Android 对每个进程的内存使用有限制,理解这些限制对开发至关重要。

1.2.1 内存限制机制

kotlin
// 获取应用最大内存(KB)
val maxMemory = Runtime.getRuntime().maxMemory()
Log.d("Memory", "Max memory: ${maxMemory / 1024} KB")

// 获取实际可用内存(KB)
val memInfo = ActivityManager.MemoryInfo()
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
activityManager.getMemoryInfo(memInfo)
val availableMemory = memInfo.availMem / 1024
Log.d("Memory", "Available memory: ${availableMemory} KB")

1.2.2 不同设备的内存限制

设备类型典型内存限制备注
低端设备15-25 MB32 位架构
中端设备25-50 MB64 位架构
高端设备50-128 MB64 位架构,大内存模式
平板64-256 MB更宽松的内存限制
kotlin
// 根据设备类型获取合适的图片加载大小
fun getTargetSize(context: Context): Int {
    val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    val memInfo = ActivityManager.MemoryInfo()
    activityManager.getMemoryInfo(memInfo)
    
    // 根据总内存判断设备等级
    val totalMem = memInfo.totalMem
    return when {
        totalMem < 512L * 1024 * 1024 -> 320 * 320 // 低端
        totalMem < 1024L * 1024 * 1024 -> 640 * 640 // 中端
        else -> 1024 * 1024 // 高端
    }
}

1.2.3 内存警告与 OOM

当应用内存使用超过限制时,系统会触发内存警告或 OOM(Out Of Memory)错误。

kotlin
class MemoryWarningReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action == Intent.ACTION_MEMORY_USAGE_CHANGED) {
            // 收到内存警告,需要释放内存
            releaseMemory()
        }
    }
    
    private fun releaseMemory() {
        // 1. 清除缓存
        imageCache.clear()
        
        // 2. 释放 Bitmap
        bitmap?.recycle()
        bitmap = null
        
        // 3. 停止后台任务
        backgroundTask.cancel()
        
        // 4. 释放单例引用
        singleton?.close()
    }
}

Manifest 配置内存警告接收器:

xml
<receiver android:name=".MemoryWarningReceiver">
    <intent-filter>
        <action android:name="android.intent.action.MEMORY_USAGE_CHANGED" />
    </intent-filter>
</receiver>

1.3 内存分配策略

1.3.1 堆内存分配

TLAB(Thread Local Allocation Buffer)

ART 为每个线程分配本地分配缓冲区,减少多线程分配时的锁竞争。

TLAB 分配流程:
1. 线程需要分配对象
2. 首先从 TLAB 分配(无锁)
3. TLAB 耗尽时,从主堆分配并填充 TLAB
4. 主堆耗尽时触发 GC

对象分配优化

kotlin
// 优化前:频繁的临时对象创建
fun process(data: List<Int>): List<Int> {
    val result = mutableListOf<Int>()
    for (item in data) {
        // 每次都创建新的对象
        val temp = TempObject(item)
        val doubled = temp.process()
        result.add(doubled)
    }
    return result
}

// 优化后:对象复用
class TempObject {
    var value: Int = 0
    
    fun process(): Int {
        value *= 2
        return value
    }
    
    fun reset(value: Int) {
        this.value = value
    }
}

fun processOptimized(data: List<Int>): List<Int> {
    val result = mutableListOf<Int>()
    val temp = TempObject()  // 复用同一个对象
    
    for (item in data) {
        temp.reset(item)
        result.add(temp.process())
    }
    return result
}

1.4 内存统计与监控

1.4.1 运行时内存信息

kotlin
class MemoryMonitor {
    companion object {
        private val runtime = Runtime.getRuntime()
        
        fun logMemoryInfo(tag: String = "Memory") {
            val used = runtime.totalMemory() - runtime.freeMemory()
            val total = runtime.totalMemory()
            val max = runtime.maxMemory()
            
            Log.d(tag, """
                |Heap 使用统计:
                |  Used: ${(used / 1024 / 1024)} MB
                |  Total: ${(total / 1024 / 1024)} MB
                |  Max: ${(max / 1024 / 1024)} MB
                |  Usage: ${String.format("%.2f", (used.toDouble() / max) * 100)}%
            """.trimMargin())
        }
    }
}

1.4.2 系统级内存监控

kotlin
class SystemMemoryMonitor(private val context: Context) {
    private val activityManager = 
        context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    
    fun logSystemMemory() {
        val memInfo = ActivityManager.MemoryInfo()
        activityManager.getMemoryInfo(memInfo)
        
        val totalMem = memInfo.totalMem / 1024 / 1024
        val availMem = memInfo.availMem / 1024 / 1024
        val lowMemory = memInfo.lowMemory
        
        Log.d("SystemMemory", """
            |系统内存状态:
            |  Total: $totalMem MB
            |  Available: $availMem MB
            |  Low Memory: $lowMemory
        """.trimMargin())
    }
}

二、Dalvik/ART虚拟机内存管理

2.1 Dalvik vs ART

Android 4.4 之前使用 Dalvik 虚拟机,4.4 开始引入 ART(Android Runtime)作为可选项,5.0 之后 ART 成为唯一运行时。

2.1.1 核心差异

特性DalvikART
执行方式JIT(即时编译)AOT(提前编译)+ JIT
启动速度较慢更快
运行时性能一般更优
安装速度慢(需要编译)
内存占用较低较高
GC 策略复制算法为主并发标记清理
Dalvik 执行流程:
  Smali 代码 → JIT 编译 → 机器码 → 执行

ART 执行流程:
  Smali 代码 → AOT 编译 → 机器码 → 执行

            JIT 优化(运行时)

2.1.2 ART 架构

ART 运行时架构:

┌──────────────────────────────────────────┐
│ 应用层                                    │
│ - Java/Kotlin 代码                        │
├──────────────────────────────────────────┤
│ ART 运行时                                │
│ ├─ 编译器                                 │
│ │  ├─ AOT 编译器(安装时)                │
│ │  └─ JIT 编译器(运行时)                │
│ ├─ 垃圾回收器                             │
│ │  ├─ 分代 GC                            │
│ │  └─ 并发 GC                            │
│ ├─ 类加载器                               │
│ └─ 运行时库                               │
├──────────────────────────────────────────┤
│ Android 框架层                            │
├──────────────────────────────────────────┤
│ Linux 内核                                │
├──────────────────────────────────────────┤
│ 硬件抽象层 (HAL)                         │
├──────────────────────────────────────────┤
│ 硬件层                                    │
└──────────────────────────────────────────┘

2.2 ART 内存布局

2.2.1 堆内存分区

ART 将堆内存划分为多个区域,每个区域有不同的管理策略。

ART 堆内存布局:

┌─────────────────────────────────────┐
│ 对象空间 (Object Space)            │
├─────────────────────────────────────┤
│ 老年代 (Old Generation)            │ ← 长生命周期对象
│ - 标记清理算法                      │
│ - 并发收集                          │
├─────────────────────────────────────┤
│ 新生代 (Young Generation)          │ ← 短生命周期对象
│ ├─ 分配缓冲区 (Allocation Buffer)   │
│ └─ 非分配缓冲区 (Non-Alloc Buffer)  │
├─────────────────────────────────────┤
│ 原生堆 (Native Heap)               │
│ - 线程栈                            │
│ - JNI 对象                          │
│ - Native 库数据                     │
├─────────────────────────────────────┤
│ 线程本地分配缓冲区 (TLAB)          │
├─────────────────────────────────────┤
│ 类空间 (Class Space)               │
│ - 类元数据                          │
│ - 方法代码                          │
└─────────────────────────────────────┘

2.2.2 分代垃圾回收

ART 使用分代垃圾回收策略,根据对象生命周期划分不同区域。

新生代(Young Generation)

存放新创建的对象,大部分对象在此区域死亡。

kotlin
// 新生代特点:
// - 对象存活时间短
// - 使用复制算法
// - 回收速度快
// - Stop-The-World 时间短

// 示例:临时对象在新生代创建和销毁
fun createTempObjects(): List<Int> {
    val tempObjects = mutableListOf<Int>()
    for (i in 1..1000) {
        tempObjects.add(i)  // 大量短生命周期对象
    }
    // 方法结束后,这些对象可以被快速回收
    return tempObjects
}

老年代(Old Generation)

存放长期存活的对象,经过多次 GC 后晋升到老年代。

kotlin
// 老年代特点:
// - 对象存活时间长
// - 使用标记清理或标记整理算法
// - 回收速度慢但频率低
// - 支持并发收集

// 示例:长期存活的对象
class ApplicationData {
    private val userCache = WeakHashMap<String, UserData>()
    private val config: Config = Config.load()
    
    // 这些对象生命周期与应用相同,会晋升到老年代
    fun getUser(key: String): UserData? {
        return userCache[key]
    }
}

2.3 垃圾回收机制

2.3.1 GC 算法

ART 实现了多种 GC 算法:

1. 复制算法(Copying)

用于新生代,将存活的对象复制到新的空间。

复制算法流程:
  Eden 区      Survivor 区 A   Survivor 区 B
  ┌──────┐    ┌──────┐       ┌──────┐
  │ 新对象 │    │ 空     │       │ 空     │
  └──────┘    └──────┘       └──────┘
        ↓ GC 后
  ┌──────┐    ┌──────┐       ┌──────┐
  │ 空     │    │ 存活   │       │ 空     │
  └──────┘    └──────┘       └──────┘

2. 标记清理(Mark-Sweep)

用于老年代,标记存活对象,清理未标记对象。

标记清理流程:
  Step 1: 标记阶段
  ┌──────┐ ┌──────┐ ┌──────┐
  │ √ 对象 │ │ × 对象 │ │ √ 对象 │  → 标记存活
  └──────┘ └──────┘ └──────┘
  
  Step 2: 清理阶段
  ┌──────┐ ┌──────┐ ┌──────┐
  │ 对象  │ │ 空     │ │ 对象  │  → 回收未标记
  └──────┘ └──────┘ └──────┘

3. 标记整理(Mark-Compact)

标记存活对象,并将它们移动到一端,解决内存碎片问题。

标记整理流程:
  Step 1: 标记
  ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
  │ √ 对象 │ │ × 对象 │ │ √ 对象 │ │ × 对象 │
  └──────┘ └──────┘ └──────┘ └──────┘
  
  Step 2: 整理
  ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
  │ 对象  │ │ 对象  │ │ 空     │ │ 空     │
  └──────┘ └──────┘ └──────┘ └──────┘

2.3.2 GC 类型

ART 提供多种 GC 类型,针对不同场景优化。

1. Concurrent Copying GC

并发复制垃圾回收,在后台线程执行大部分工作。

kotlin
// 适用于新生代,大部分工作并发执行
// 减少主线程停顿时间

2. Concurrent Mark Compact GC

并发标记整理垃圾回收,解决内存碎片问题。

kotlin
// 适用于老年代,支持并发标记和整理
// 避免 Stop-The-World 长时间停顿

3. Background Mark Compact GC

后台标记整理垃圾回收,完全在后台执行。

kotlin
// 完全后台执行,对应用性能影响最小
// 但可能产生内存碎片

2.3.3 GC 触发条件

kotlin
// GC 可能触发的场景:

// 1. 堆内存不足
fun allocateLargeObject() {
    val data = ByteArray(10 * 1024 * 1024) // 分配 10MB
    // 如果堆空间不足,触发 GC
}

// 2. 手动请求(不保证立即执行)
fun requestGC() {
    System.gc() // 建议 JVM 执行 GC
    // 注意:这只是建议,JVM 可能忽略
}

// 3. 分配失败
fun createObjectsUntilGC() {
    val objects = mutableListOf<Any>()
    while (true) {
        objects.add(Any())
        // 分配失败时触发 GC
    }
}

// 4. 弱引用对象
class WeakContainer {
    val weakRef = WeakReference(Any())
    // GC 时,弱引用对象可能被回收
}

// 5. 软引用对象(内存不足时)
class SoftContainer {
    val softRef = SoftReference(Any())
    // 内存不足时,软引用对象优先被回收
}

// 6. 虚引用对象(用于通知)
class VirtualContainer : ReferenceQueue<Any>() {
    val virtualRef = PhantomReference(Any(), this)
    // GC 后,虚引用被加入队列,可以收到通知
}

2.4 内存屏障与可见性

2.4.1 内存屏障

内存屏障确保多线程环境下的内存可见性。

kotlin
// 内存屏障类型:
// 1. LoadLoad 屏障
// 2. StoreStore 屏障
// 3. LoadStore 屏障
// 4. StoreLoad 屏障

// volatile 关键字包含内存屏障
class Counter {
    @Volatile var count: Int = 0
    
    fun increment() {
        count++ // 包含 LoadStore 和 StoreLoad 屏障
    }
}

2.4.2 并发问题示例

kotlin
// 错误示例:没有内存屏障
class BadCounter {
    var count: Int = 0 // 没有 volatile
    
    fun increment() {
        count++ // 不是原子操作
    }
}

// 正确示例:使用 volatile 和原子操作
class GoodCounter {
    @Volatile var count: Int = 0
    
    fun increment() {
        count = count + 1 // volatile 保证可见性
    }
}

// 更好的示例:使用原子类
class BestCounter {
    private val count = AtomicInteger(0)
    
    fun increment() {
        count.incrementAndGet() // 原子操作
    }
    
    fun getCount(): Int {
        return count.get()
    }
}

2.5 ART 优化技术

2.5.1 Profile 引导优化

ART 收集运行时性能配置文件,优化热点代码。

kotlin
// Profile 收集:
// 1. 运行应用
// 2. ART 记录方法调用频率
// 3. 下次安装时使用 Profile 优化

// 生成 Profile 文件
adb shell dumpsys gfxinfo <package_name>

// 应用 Profile
apksigner sign --ks key.jks --profile profile.txt app.apk

2.5.2 方法内联优化

ART 将频繁调用的小方法内联到调用处。

kotlin
// 适合内联的小方法
class Calculator {
    fun add(a: Int, b: Int): Int {
        return a + b // 可能被内联
    }
    
    fun calculate(x: Int, y: Int, z: Int): Int {
        // add 方法可能被内联到这里
        return add(x, add(y, z))
    }
}

// 不适合内联的大方法
class Processor {
    fun process(data: List<Int>): List<Int> {
        // 方法体较大,不会被内联
        return data.map { it * 2 }
    }
}

2.5.3 逃逸分析

ART 分析对象是否逃逸出方法,决定分配策略。

kotlin
// 不逃逸的对象(可栈分配)
fun createLocalObject(): Int {
    val local = LocalObject()
    local.value = 42
    return local.value
}

class LocalObject {
    var value: Int = 0
}

// 逃逸的对象(必须堆分配)
fun createEscapedObject(): LocalObject {
    val escaped = LocalObject()
    escaped.value = 42
    return escaped // 返回给调用者,逃逸
}

三、LruCache 原理和使用

3.1 LruCache 概述

LruCache 是 Android 提供的高效内存缓存实现,基于 LinkedHashMap 和 LRU(Least Recently Used,最近最少使用)算法。

3.1.1 核心特性

  • 自动管理内存:根据最大容量自动淘汰最久未使用的条目
  • 线程安全:所有方法都进行了同步处理
  • 高性能:O(1) 时间复杂度的 put 和 get 操作
  • 容量可控:通过 maxSize 参数控制缓存大小

3.1.2 LRU 算法原理

LRU 算法核心思想:
1. 最近使用的数据放在缓存的头部
2. 最久未使用的数据放在缓存的尾部
3. 当缓存满时,从尾部淘汰数据

示例:容量为 3 的 LRU 缓存

初始状态:[]
put(1, "A") → [1-A]
put(2, "B") → [1-A, 2-B]
put(3, "C") → [1-A, 2-B, 3-C]
get(1)      → [2-B, 3-C, 1-A]  // 访问 1,移到头部
put(4, "D") → [3-C, 1-A, 4-D]  // 淘汰 2-B

3.2 LruCache 源码分析

3.2.1 核心数据结构

java
public class LruCache<K, V> {
    // 最大容量(单位由构造函数决定)
    private final int maxSize;
    
    // 当前缓存大小
    private long size;
    
    // 条目数量
    private int entries;
    
    // 核心数据结构:LinkedHashMap
    // accessOrder=true 表示按访问顺序排序
    // 实现了 LRU 淘汰策略
    private final LinkedHashMap<K, V> map;
    
    // 同步锁
    private final Object lock = new Object();
}

3.2.2 LinkedHashMap 实现

java
// LinkedHashMap 关键特性
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> {
    
    // 双向链表节点
    private static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;
    }
    
    // accessOrder=true 表示按访问顺序
    // 用于实现 LRU 策略
    private final boolean accessOrder;
    
    // put 方法:更新访问顺序
    V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean eviction) {
        // ... 省略部分代码
        if (accessOrder) {
            afterLast(e); // 将节点移到链表尾部
        }
        // ...
    }
    
    // get 方法:更新访问顺序
    V get(Object key) {
        V value = super.get(key);
        if (accessOrder && value != null) {
            afterLast(getNode(key)); // 更新访问顺序
        }
        return value;
    }
}

3.2.3 淘汰机制

java
// 检查是否需要淘汰条目
void trimToSize(int maxSize) {
    while (true) {
        LinkedHashMap.Entry<K, V> toEvict = map.eldest();
        if (toEvict == null) {
            break; // 缓存未满
        }
        
        // 移除最旧的条目
        map.remove(toEvict.getKey());
        size -= sizeOf(toEvict.getKey(), toEvict.getValue());
        entries--;
        
        // 允许子类自定义回收逻辑
        entryRemoved(true, toEvict.getKey(), toEvict.getValue(),
                     map.get(toEvict.getKey()));
        
        if (size <= maxSize) {
            break;
        }
    }
}

3.3 LruCache 使用指南

3.3.1 基本使用

kotlin
// 1. 创建 LruCache 实例
class ImageCache : LruCache<String, Bitmap>(16) {
    // maxSize = 16MB
    override fun sizeOf(key: String, value: Bitmap): Int {
        // 返回条目实际占用的内存大小
        return value.byteCount
    }
    
    override fun entryRemoved(
        evicted: Boolean,
        key: String,
        oldValue: Bitmap,
        newValue: Bitmap?
    ) {
        // 条目被移除时的回调
        if (evicted) {
            oldValue.recycle() // 释放 Bitmap 内存
        }
    }
}

// 2. 使用缓存
val imageCache = ImageCache()
val bitmap = loadBitmapFromNetwork(url)
imageCache.put(url, bitmap)

// 3. 获取缓存
val cachedBitmap = imageCache.get(url)

3.3.2 计算合适的缓存大小

kotlin
object CacheSizeCalculator {
    /**
     * 计算合适的 LruCache 大小
     * 通常使用系统最大内存的 1/8 到 1/16
     */
    fun calculateMaxCacheSize(context: Context): Int {
        val runtime = Runtime.getRuntime()
        val maxMemory = runtime.maxMemory()
        
        // 获取可用内存
        val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val memInfo = ActivityManager.MemoryInfo()
        activityManager.getMemoryInfo(memInfo)
        
        val availableMemory = memInfo.availMem
        
        // 使用较小值:最大内存的 1/8 或可用内存的 50%
        return minOf(
            maxMemory / 8,
            availableMemory / 2
        ).toInt()
    }
}

// 使用
val maxCacheSize = CacheSizeCalculator.calculateMaxCacheSize(context)
val imageCache = object : LruCache<String, Bitmap>(maxCacheSize) {
    override fun sizeOf(key: String, value: Bitmap): Int {
        return value.byteCount
    }
}

3.3.3 高级使用场景

场景 1:图片缓存

kotlin
class ImageCache(private val context: Context) {
    private val cache: LruCache<String, Bitmap>
    
    init {
        val maxMemory = Runtime.getRuntime().maxMemory() / 8
        cache = object : LruCache<String, Bitmap>(maxMemory) {
            override fun sizeOf(key: String, value: Bitmap): Int {
                return value.byteCount
            }
            
            override fun entryRemoved(
                evicted: Boolean,
                key: String,
                oldValue: Bitmap,
                newValue: Bitmap?
            ) {
                if (evicted && !oldValue.isRecycled) {
                    oldValue.recycle()
                }
            }
        }
    }
    
    fun getBitmap(url: String): Bitmap? {
        return cache.get(url)
    }
    
    fun putBitmap(url: String, bitmap: Bitmap) {
        cache.put(url, bitmap)
    }
    
    fun clear() {
        cache.evictAll()
    }
    
    fun size(): Int {
        return cache.size()
    }
    
    fun cacheSize(): Long {
        return cache.size
    }
}

场景 2:数据缓存

kotlin
class DataCache {
    private val cache = object : LruCache<String, Data>(10 * 1024 * 1024) {
        override fun sizeOf(key: String, value: Data): Int {
            // 计算 Data 对象的大小
            return value.estimatedSize()
        }
    }
    
    inner class Data(
        val id: String,
        val name: String,
        val description: String
    ) {
        fun estimatedSize(): Int {
            return id.length + name.length + description.length
        }
    }
    
    fun getData(id: String): Data? {
        return cache.get(id)
    }
    
    fun putData(id: String, data: Data) {
        cache.put(id, data)
    }
}

场景 3:多级别缓存

kotlin
class MultiLevelCache {
    // L1: 内存缓存(最快)
    private val memoryCache = LruCache<String, Any>(10 * 1024 * 1024) {
        override fun sizeOf(key: String, value: Any): Int {
            return value.estimatedSize()
        }
    }
    
    // L2: 磁盘缓存(持久化)
    private val diskCache: DiskLruCache
    
    init {
        val cacheDir = ContextUtil.cacheDir / "disk"
        diskCache = DiskLruCache.open(cacheDir, 1, 1, 50 * 1024 * 1024)
    }
    
    fun get(key: String): Any? {
        // 先查内存
        var result = memoryCache.get(key)
        if (result != null) return result
        
        // 再查磁盘
        result = readFromDisk(key)
        if (result != null) {
            memoryCache.put(key, result)
        }
        
        return result
    }
    
    fun put(key: String, value: Any) {
        memoryCache.put(key, value)
        writeToDisk(key, value)
    }
    
    private fun readFromDisk(key: String): Any? {
        // 实现从磁盘读取
        return null
    }
    
    private fun writeToDisk(key: String, value: Any) {
        // 实现写入磁盘
    }
}

3.4 LruCache 最佳实践

3.4.1 容量设置原则

kotlin
// 原则 1:不超过系统最大内存的 1/8
val maxSize = Runtime.getRuntime().maxMemory() / 8

// 原则 2:考虑设备内存差异
val maxCacheSize = when {
    isLowEndDevice() -> 10 * 1024 * 1024  // 10MB
    isMidRangeDevice() -> 20 * 1024 * 1024 // 20MB
    else -> 30 * 1024 * 1024              // 30MB
}

// 原则 3:根据缓存类型调整
val bitmapCacheSize = maxMemory / 8   // 图片缓存
val dataCacheSize = maxMemory / 16    // 数据缓存
val networkCacheSize = maxMemory / 32 // 网络缓存

3.4.2 内存管理

kotlin
class ManagedCache : LruCache<String, Bitmap>(16 * 1024 * 1024) {
    override fun sizeOf(key: String, value: Bitmap): Int {
        return value.byteCount
    }
    
    override fun entryRemoved(
        evicted: Boolean,
        key: String,
        oldValue: Bitmap,
        newValue: Bitmap?
    ) {
        // 确保 Bitmap 被正确回收
        if (evicted && !oldValue.isRecycled) {
            oldValue.recycle()
        }
    }
    
    override fun beforeEviction(maxSize: Int, size: Int): Int {
        // 在淘汰前调用,可以调整最大容量
        return maxSize
    }
    
    // 主动释放缓存
    fun releaseMemory() {
        evictAll()
    }
}

3.4.3 线程安全

kotlin
// LruCache 是线程安全的,但需要注意:

// 1. put 和 get 操作是原子的
val bitmap = cache.get(url) ?: loadBitmap(url).also {
    cache.put(url, it)
}

// 2. 批量操作需要加锁
synchronized(cache) {
    cache.evictAll()
    cache.putAll(newCacheItems)
}

// 3. 统计操作
val cacheStats = StatsHolder()
synchronized(cache) {
    cacheStats.size = cache.size()
    cacheStats.cacheSize = cache.size
}

3.5 自定义 LruCache 扩展

3.5.1 添加过期时间

kotlin
class ExpiringLruCache<K, V>(maxSize: Int) : LruCache<K, V>(maxSize) {
    
    private data class CacheEntry<T>(
        val value: T,
        val expireTime: Long
    )
    
    private val expireTimes = ConcurrentHashMap<K, Long>()
    
    fun putWithExpiry(key: K, value: V, expireSeconds: Long) {
        val expireTime = System.currentTimeMillis() + expireSeconds * 1000
        expireTimes[key] = expireTime
        super.put(key, value)
    }
    
    override fun get(key: K): V? {
        val expireTime = expireTimes[key]
        if (expireTime != null && System.currentTimeMillis() > expireTime) {
            // 已过期,移除
            remove(key)
            expireTimes.remove(key)
            return null
        }
        return super.get(key)
    }
    
    fun isExpired(key: K): Boolean {
        val expireTime = expireTimes[key]
        return expireTime != null && System.currentTimeMillis() > expireTime
    }
    
    fun clearExpired() {
        val expiredKeys = expireTimes.entries
            .filter { System.currentTimeMillis() > it.value }
            .map { it.key }
        
        for (key in expiredKeys) {
            remove(key)
            expireTimes.remove(key)
        }
    }
}

3.5.2 添加统计功能

kotlin
class StatsLruCache<K, V>(maxSize: Int) : LruCache<K, V>(maxSize) {
    
    private var hitCount = 0L
    private var missCount = 0L
    private val lock = Object()
    
    override fun get(key: K): V? {
        val value = super.get(key)
        synchronized(lock) {
            if (value != null) {
                hitCount++
            } else {
                missCount++
            }
        }
        return value
    }
    
    fun getStats(): CacheStats {
        synchronized(lock) {
            return CacheStats(
                hits = hitCount,
                misses = missCount,
                hitRate = if (hitCount + missCount > 0) {
                    hitCount.toDouble() / (hitCount + missCount)
                } else 0.0
            )
        }
    }
    
    data class CacheStats(
        val hits: Long,
        val misses: Long,
        val hitRate: Double
    )
}

四、MemoryCache 实现

4.1 MemoryCache 设计模式

MemoryCache 是基于 LruCache 的高级缓存实现,提供额外的功能如过期时间、优先级等。

4.1.1 基本设计

kotlin
interface MemoryCache<K, V> {
    // 基本操作
    fun get(key: K): V?
    fun put(key: K, value: V): V?
    fun remove(key: K): V?
    
    // 批量操作
    fun putAll(map: Map<K, V>)
    fun removeAll(keys: Collection<K>)
    
    // 统计信息
    val size: Int
    val cacheSize: Long
    
    // 管理操作
    fun clear()
    fun evictAll()
}

4.1.2 完整实现

kotlin
class SimpleMemoryCache<K, V>(
    private val maxSize: Long
) : MemoryCache<K, V> {
    
    private val cache = LinkedHashMap<K, V>(16, 0.75f, true)
    private var currentSize = 0L
    private val sizeCalculator: (K, V) -> Long
    
    init {
        // 默认大小计算
        this.sizeCalculator = { _, value -> 
            when (value) {
                is Bitmap -> value.byteCount.toLong()
                is ByteArray -> value.size.toLong()
                else -> Runtime.getRuntime().objectSize(value)
            }
        }
    }
    
    @Synchronized
    override fun get(key: K): V? {
        val value = cache[key]
        return value
    }
    
    @Synchronized
    override fun put(key: K, value: V): V? {
        val oldSize = sizeCalculator(key, value)
        
        // 移除旧的条目
        val oldValue = cache[key]
        if (oldValue != null) {
            currentSize -= sizeCalculator(key, oldValue)
        }
        
        // 添加新条目
        cache[key] = value
        currentSize += oldSize
        
        // 淘汰旧条目
        trimToSize()
        
        return oldValue
    }
    
    @Synchronized
    override fun remove(key: K): V? {
        val value = cache.remove(key)
        if (value != null) {
            currentSize -= sizeCalculator(key, value)
        }
        return value
    }
    
    @Synchronized
    override fun putAll(map: Map<K, V>) {
        for ((key, value) in map) {
            put(key, value)
        }
    }
    
    @Synchronized
    override fun removeAll(keys: Collection<K>) {
        for (key in keys) {
            remove(key)
        }
    }
    
    @Synchronized
    override fun clear() {
        cache.clear()
        currentSize = 0
    }
    
    @Synchronized
    override fun evictAll() {
        cache.clear()
        currentSize = 0
    }
    
    override val size: Int
        get() = cache.size
    
    override val cacheSize: Long
        get() = currentSize
    
    // 淘汰策略
    private fun trimToSize() {
        while (currentSize > maxSize && cache.isNotEmpty()) {
            // 移除最旧的条目(LRU)
            val eldest = cache.keys.first()
            val value = cache.remove(eldest)
            currentSize -= sizeCalculator(eldest, value!!)
        }
    }
}

4.2 高级 MemoryCache 实现

4.2.1 带过期时间的缓存

kotlin
class ExpiringMemoryCache<K, V>(
    maxSize: Long,
    private val defaultExpireSeconds: Long = 60 * 60 // 默认 1 小时
) : MemoryCache<K, V> {
    
    private data class CacheEntry<T>(
        val value: T,
        val expireTime: Long
    )
    
    private val cache = LinkedHashMap<K, CacheEntry<V>>(16, 0.75f, true)
    private val expireMap = ConcurrentHashMap<K, Long>()
    private var currentSize = 0L
    private val sizeCalculator: (K, V) -> Long = { _, value -> 
        when (value) {
            is Bitmap -> value.byteCount.toLong()
            else -> 1024L // 默认估计
        }
    }
    
    override fun get(key: K): V? {
        synchronized(this) {
            val entry = cache[key]
            if (entry != null && !isExpired(entry.expireTime)) {
                return entry.value
            }
            // 过期则移除
            if (entry != null) {
                remove(key)
            }
            return null
        }
    }
    
    override fun put(key: K, value: V): V? {
        return putWithExpiry(key, value, defaultExpireSeconds)
    }
    
    fun putWithExpiry(key: K, value: V, expireSeconds: Long): V? {
        synchronized(this) {
            val expireTime = System.currentTimeMillis() + expireSeconds * 1000
            val oldEntry = cache[key]
            
            if (oldEntry != null) {
                currentSize -= sizeCalculator(key, oldEntry.value)
            }
            
            val newEntry = CacheEntry(value, expireTime)
            cache[key] = newEntry
            expireMap[key] = expireTime
            currentSize += sizeCalculator(key, value)
            
            trimToSize()
            return oldEntry?.value
        }
    }
    
    override fun remove(key: K): V? {
        synchronized(this) {
            val entry = cache.remove(key)
            expireMap.remove(key)
            if (entry != null) {
                currentSize -= sizeCalculator(key, entry.value)
            }
            return entry?.value
        }
    }
    
    override fun clear() {
        synchronized(this) {
            cache.clear()
            expireMap.clear()
            currentSize = 0
        }
    }
    
    override val size: Int
        get() = cache.size
    
    override val cacheSize: Long
        get() = currentSize
    
    // 清理所有过期条目
    fun clearExpired() {
        synchronized(this) {
            val currentTime = System.currentTimeMillis()
            val expiredKeys = cache.entries
                .filter { it.value.expireTime < currentTime }
                .map { it.key }
            
            for (key in expiredKeys) {
                val entry = cache.remove(key)
                if (entry != null) {
                    currentSize -= sizeCalculator(key, entry.value)
                }
                expireMap.remove(key)
            }
        }
    }
    
    // 设置特定条目的过期时间
    fun setExpiry(key: K, expireSeconds: Long) {
        synchronized(this) {
            val expireTime = System.currentTimeMillis() + expireSeconds * 1000
            val entry = cache[key]
            if (entry != null) {
                expireMap[key] = expireTime
            }
        }
    }
    
    // 检查条目是否过期
    fun isExpired(key: K): Boolean {
        val expireTime = expireMap[key]
        return expireTime != null && System.currentTimeMillis() > expireTime
    }
    
    private fun isExpired(expireTime: Long): Boolean {
        return System.currentTimeMillis() > expireTime
    }
    
    private fun trimToSize() {
        while (currentSize > maxSize && cache.isNotEmpty()) {
            val eldest = cache.keys.first()
            val entry = cache.remove(eldest)
            if (entry != null) {
                currentSize -= sizeCalculator(eldest, entry.value)
            }
            expireMap.remove(eldest)
        }
    }
}

4.2.2 优先级缓存

kotlin
class PriorityMemoryCache<K, V>(
    maxSize: Long
) : MemoryCache<K, V> {
    
    enum class Priority {
        LOW, NORMAL, HIGH
    }
    
    private data class PriorityEntry<T>(
        val value: T,
        val priority: Priority
    )
    
    private val lowPriorityCache = LinkedHashMap<K, V>(16, 0.75f, true)
    private val normalPriorityCache = LinkedHashMap<K, V>(16, 0.75f, true)
    private val highPriorityCache = LinkedHashMap<K, V>(16, 0.75f, true)
    
    private var lowPrioritySize = 0L
    private var normalPrioritySize = 0L
    private var highPrioritySize = 0L
    
    private val sizeCalculator: (K, V) -> Long = { _, value ->
        when (value) {
            is Bitmap -> value.byteCount.toLong()
            is ByteArray -> value.size.toLong()
            else -> 1024L
        }
    }
    
    @Synchronized
    override fun get(key: K): V? {
        return highPriorityCache[key]
            ?: normalPriorityCache[key]
            ?: lowPriorityCache[key]
    }
    
    @Synchronized
    override fun put(key: K, value: V): V? {
        return put(key, value, Priority.NORMAL)
    }
    
    fun put(key: K, value: V, priority: Priority): V? {
        synchronized(this) {
            val size = sizeCalculator(key, value)
            var oldValue: V? = null
            
            return when (priority) {
                Priority.LOW -> {
                    // 检查是否有旧条目需要移除
                    oldValue = removeKeyFromAllCaches(key)
                    addToCache(lowPriorityCache, key, value, size)
                    lowPrioritySize += size
                    trimLowPriority()
                }
                Priority.NORMAL -> {
                    oldValue = removeKeyFromAllCaches(key)
                    addToCache(normalPriorityCache, key, value, size)
                    normalPrioritySize += size
                    trimNormalPriority()
                }
                Priority.HIGH -> {
                    oldValue = removeKeyFromAllCaches(key)
                    addToCache(highPriorityCache, key, value, size)
                    highPrioritySize += size
                    trimHighPriority()
                }
            }
        }
    }
    
    @Synchronized
    override fun remove(key: K): V? {
        return removeKeyFromAllCaches(key)
    }
    
    @Synchronized
    override fun clear() {
        lowPriorityCache.clear()
        normalPriorityCache.clear()
        highPriorityCache.clear()
        lowPrioritySize = 0
        normalPrioritySize = 0
        highPrioritySize = 0
    }
    
    override val size: Int
        get() = lowPriorityCache.size + normalPriorityCache.size + highPriorityCache.size
    
    override val cacheSize: Long
        get() = lowPrioritySize + normalPrioritySize + highPrioritySize
    
    // 辅助方法
    private fun removeKeyFromAllCaches(key: K): V? {
        var value: V? = null
        value = highPriorityCache.remove(key)?.also {
            highPrioritySize -= sizeCalculator(key, it)
        }
        if (value == null) {
            value = normalPriorityCache.remove(key)?.also {
                normalPrioritySize -= sizeCalculator(key, it)
            }
        }
        if (value == null) {
            value = lowPriorityCache.remove(key)?.also {
                lowPrioritySize -= sizeCalculator(key, it)
            }
        }
        return value
    }
    
    private fun addToCache(cache: LinkedHashMap<K, V>, key: K, value: V, size: Long) {
        cache[key] = value
    }
    
    // 淘汰策略:优先淘汰低优先级
    private fun trimLowPriority() {
        val totalSize = lowPrioritySize + normalPrioritySize + highPrioritySize
        while (totalSize > maxSize && lowPriorityCache.isNotEmpty()) {
            val eldest = lowPriorityCache.keys.first()
            val value = lowPriorityCache.remove(eldest)
            lowPrioritySize -= sizeCalculator(eldest, value!!)
        }
    }
    
    private fun trimNormalPriority() {
        val totalSize = lowPrioritySize + normalPrioritySize + highPrioritySize
        if (totalSize > maxSize) {
            trimLowPriority()
            if (totalSize > maxSize && normalPriorityCache.isNotEmpty()) {
                val eldest = normalPriorityCache.keys.first()
                val value = normalPriorityCache.remove(eldest)
                normalPrioritySize -= sizeCalculator(eldest, value!!)
            }
        }
    }
    
    private fun trimHighPriority() {
        val totalSize = lowPrioritySize + normalPrioritySize + highPrioritySize
        if (totalSize > maxSize) {
            trimLowPriority()
            trimNormalPriority()
            if (totalSize > maxSize && highPriorityCache.isNotEmpty()) {
                val eldest = highPriorityCache.keys.first()
                val value = highPriorityCache.remove(eldest)
                highPrioritySize -= sizeCalculator(eldest, value!!)
            }
        }
    }
}

4.3 双缓存实现

kotlin
class DoubleCache<K, V>(
    memoryCacheSize: Long,
    diskCacheDir: File
) {
    private val memoryCache = SimpleMemoryCache<K, V>(memoryCacheSize)
    private val diskCache = DiskLruCache(diskCacheDir)
    
    fun get(key: K): V? {
        // 先查内存缓存
        var value = memoryCache.get(key)
        if (value != null) return value
        
        // 再查磁盘缓存
        value = diskCache.read(key)?.let { deserialize(it) }
        if (value != null) {
            // 写入内存缓存
            memoryCache.put(key, value)
        }
        
        return value
    }
    
    fun put(key: K, value: V) {
        // 写入内存缓存
        memoryCache.put(key, value)
        
        // 异步写入磁盘缓存
        AsyncTask.execute {
            diskCache.write(key, serialize(value))
        }
    }
    
    fun clear() {
        memoryCache.clear()
        diskCache.clear()
    }
    
    // 序列化和反序列化
    private fun serialize(value: V): ByteArray {
        // 实现序列化逻辑
        return byteArrayOf()
    }
    
    private fun deserialize(data: ByteArray): V? {
        // 实现反序列化逻辑
        return null
    }
}

五、Bitmap 内存管理

5.1 Bitmap 基础

5.1.1 Bitmap 内存计算

kotlin
// Bitmap 内存计算公式:
// 内存 = 宽 * 高 * 每个像素的字节数

class BitmapMemoryCalculator {
    companion object {
        /**
         * 计算 Bitmap 占用的内存大小
         * @param width 宽度
         * @param height 高度
         * @param config 配置(决定每个像素的字节数)
         */
        fun calculateBitmapSize(
            width: Int,
            height: Int,
            config: Bitmap.Config = Bitmap.Config.ARGB_8888
        ): Int {
            val bytesPerPixel = when (config) {
                Bitmap.Config.ALPHA_8 -> 1
                Bitmap.Config.ARGB_4444, 
                Bitmap.Config.RGB_565 -> 2
                Bitmap.Config.ARGB_8888,
                Bitmap.Config.RGBA_F16,
                Bitmap.Config.HARDWARE -> 4
                else -> 4
            }
            return width * height * bytesPerPixel
        }
        
        /**
         * 计算 Bitmap 内存(MB)
         */
        fun calculateBitmapSizeMB(
            width: Int,
            height: Int,
            config: Bitmap.Config = Bitmap.Config.ARGB_8888
        ): Double {
            return calculateBitmapSize(width, height, config) / 
                   (1024.0 * 1024.0)
        }
    }
}

// 示例:计算不同分辨率的 Bitmap 内存占用
fun printBitmapMemoryExamples() {
    val sizes = listOf(
        480 to 320,   // WVGA
        1280 to 720,  // HD
        1920 to 1080, // Full HD
        3840 to 2160  // 4K
    )
    
    for ((width, height) in sizes) {
        val sizeBytes = BitmapMemoryCalculator.calculateBitmapSize(width, height)
        val sizeMB = BitmapMemoryCalculator.calculateBitmapSizeMB(width, height)
        Log.d("BitmapSize", "${width}x${height}: ${sizeBytes / 1024}KB = $sizeMB MB")
    }
}

5.1.2 Bitmap 配置选择

kotlin
// 不同配置的选择策略
class BitmapConfigSelector {
    companion object {
        /**
         * 根据需求选择合适的 Bitmap 配置
         */
        fun selectConfig(requirements: ConfigRequirements): Bitmap.Config {
            return when {
                // 只需要透明度,不需要颜色
                requirements.needsTransparency && !requirements.needsColor -> 
                    Bitmap.Config.ALPHA_8
                
                // 需要颜色但不需要透明度,且颜色要求不高
                requirements.needsColor && !requirements.needsTransparency && 
                !requirements.highColorQuality -> 
                    Bitmap.Config.RGB_565
                
                // 需要颜色和透明度,但不需要高质量
                requirements.needsColor && requirements.needsTransparency &&
                !requirements.highColorQuality -> 
                    Bitmap.Config.ARGB_4444
                
                // 默认高质量配置
                else -> Bitmap.Config.ARGB_8888
            }
        }
    }
    
    data class ConfigRequirements(
        val needsColor: Boolean = true,
        val needsTransparency: Boolean = false,
        val highColorQuality: Boolean = true
    )
}

5.2 Bitmap 加载优化

5.2.1 解码选项

kotlin
class BitmapDecoder {
    companion object {
        /**
         * 计算合适的采样率
         */
        fun calculateSampleSize(
            options: BitmapFactory.Options,
            reqWidth: Int,
            reqHeight: Int
        ): Int {
            val height = options.outHeight
            val width = options.outWidth
            var sampleSize = 1
            
            if (height > reqHeight || width > reqWidth) {
                val halfHeight = height / 2
                val halfWidth = width / 2
                
                while (halfHeight / sampleSize >= reqHeight &&
                       halfWidth / sampleSize >= reqWidth) {
                    sampleSize *= 2
                }
            }
            
            return sampleSize
        }
        
        /**
         * 加载缩小后的 Bitmap
         */
        fun loadScaledBitmap(
            context: Context,
            resourceId: Int,
            reqWidth: Int,
            reqHeight: Int
        ): Bitmap? {
            // 第一步:仅解码图片大小
            val options = BitmapFactory.Options().apply {
                inJustDecodeBounds = true
            }
            BitmapFactory.decodeResource(
                context.resources,
                resourceId,
                options
            )
            
            // 第二步:计算采样率
            val sampleSize = calculateSampleSize(options, reqWidth, reqHeight)
            
            // 第三步:使用采样率解码
            options.inSampleSize = sampleSize
            options.inJustDecodeBounds = false
            
            return BitmapFactory.decodeResource(
                context.resources,
                resourceId,
                options
            )
        }
    }
}

5.2.2 使用 Recycle 机制

kotlin
class BitmapLifecycleManager {
    
    /**
     * 加载 Bitmap 并管理生命周期
     */
    fun loadBitmapWithLifecycle(
        bitmap: Bitmap,
        onViewDisappear: () -> Unit
    ) {
        // 设置回调
        bitmap.setHasAlpha(true) // 如果需要透明度
        
        // 在 View 消失时回收
        onViewDisappear = {
            if (!bitmap.isRecycled) {
                bitmap.recycle()
            }
        }
    }
    
    /**
     * 安全的 Bitmap 回收
     */
    fun safeRecycle(bitmap: Bitmap?) {
        if (bitmap != null && !bitmap.isRecycled) {
            bitmap.recycle()
        }
    }
    
    /**
     * 使用 WeakReference 管理 Bitmap
     */
    fun loadBitmapWithWeakReference(): WeakReference<Bitmap> {
        val bitmap = BitmapFactory.decodeFile(path)
        return WeakReference(bitmap)
    }
}

5.3 Bitmap 内存池

kotlin
class BitmapPool(private val maxSizeBytes: Int) {
    
    private val pool = SynchronizedLinkedList<Bitmap>()
    private var currentSize = 0
    
    /**
     * 从池中获取 Bitmap
     */
    fun getBitmap(width: Int, height: Int, config: Bitmap.Config): Bitmap? {
        synchronized(pool) {
            val targetSize = width * height * bytesPerPixel(config)
            
            // 查找合适大小的 Bitmap
            for (bitmap in pool) {
                if (bitmap.byteCount >= targetSize) {
                    pool.remove(bitmap)
                    currentSize -= bitmap.byteCount
                    
                    // 如果太大,创建新的合适大小的 Bitmap
                    if (bitmap.width != width || bitmap.height != height) {
                        val newBitmap = Bitmap.createBitmap(width, height, config)
                        bitmap.recycle()
                        return newBitmap
                    }
                    return bitmap
                }
            }
            
            // 没有找到合适的,创建新的
            return Bitmap.createBitmap(width, height, config)
        }
    }
    
    /**
     * 将 Bitmap 放回池中
     */
    fun putBitmap(bitmap: Bitmap) {
        if (bitmap.isRecycled) return
        
        synchronized(pool) {
            val newSize = currentSize + bitmap.byteCount
            
            if (newSize <= maxSizeBytes) {
                pool.addFirst(bitmap)
                currentSize = newSize
            } else {
                // 池已满,回收 Bitmap
                bitmap.recycle()
            }
        }
    }
    
    /**
     * 清除所有缓存
     */
    fun clear() {
        synchronized(pool) {
            for (bitmap in pool) {
                if (!bitmap.isRecycled) {
                    bitmap.recycle()
                }
            }
            pool.clear()
            currentSize = 0
        }
    }
    
    private fun bytesPerPixel(config: Bitmap.Config): Int {
        return when (config) {
            Bitmap.Config.ALPHA_8 -> 1
            Bitmap.Config.ARGB_4444, 
            Bitmap.Config.RGB_565 -> 2
            else -> 4
        }
    }
}

5.4 Bitmap 内存泄漏检测

kotlin
class BitmapLeakDetector {
    
    /**
     * 检测潜在的 Bitmap 泄漏
     */
    fun detectLeaks(): List<LeakedBitmap> {
        val leakedBitmaps = mutableListOf<LeakedBitmap>()
        
        // 使用 RefWatcher(来自 LeakCanary)
        val refWatcher = RefWatcher.create()
        
        // 检查所有 Bitmap 引用
        checkBitmapReferences(refWatcher, leakedBitmaps)
        
        return leakedBitmaps
    }
    
    /**
     * 使用 Debug 模式检查 Bitmap
     */
    fun checkInDebugMode(context: Context) {
        if (BuildConfig.DEBUG) {
            val memInfo = ActivityManager.MemoryInfo()
            val activityManager = context.getSystemService(
                Context.ACTIVITY_SERVICE
            ) as ActivityManager
            activityManager.getMemoryInfo(memInfo)
            
            val usedMem = memInfo.totalMem - memInfo.availMem
            val threshold = 50 * 1024 * 1024 // 50MB
            
            if (usedMem > threshold) {
                Log.w("BitmapLeak", "High memory usage: ${usedMem / 1024 / 1024} MB")
                triggerGCAndCheck()
            }
        }
    }
    
    private fun triggerGCAndCheck() {
        Runtime.getRuntime().gc()
        Thread.sleep(100)
        Runtime.getRuntime().gc()
    }
    
    data class LeakedBitmap(
        val bitmap: Bitmap,
        val size: Int,
        val location: String
    )
}

5.5 Bitmap 最佳实践

kotlin
class BitmapBestPractices {
    
    companion object {
        /**
         * 实践 1: 使用合适的大小
         */
        fun loadAppropriateBitmap(context: Context, imageView: ImageView): Bitmap {
            val options = BitmapFactory.Options().apply {
                inJustDecodeBounds = true
            }
            BitmapFactory.decodeFile(imagePath, options)
            
            val targetW = imageView.measuredWidth.coerceAtLeast(1)
            val targetH = imageView.measuredHeight.coerceAtLeast(1)
            
            options.inSampleSize = calculateSampleSize(
                options.outWidth,
                options.outHeight,
                targetW,
                targetH
            )
            options.inJustDecodeBounds = false
            
            return BitmapFactory.decodeFile(imagePath, options)
        }
        
        /**
         * 实践 2: 及时释放
         */
        fun useBitmapSafely(bitmap: Bitmap, work: (Bitmap) -> Unit) {
            try {
                work(bitmap)
            } finally {
                if (!bitmap.isRecycled) {
                    bitmap.recycle()
                }
            }
        }
        
        /**
         * 实践 3: 使用硬件加速
         */
        fun createHardwareBitmap(width: Int, height: Int): Bitmap {
            return Bitmap.createBitmap(
                width,
                height,
                Bitmap.Config.HARDWARE
            )
        }
        
        /**
         * 实践 4: 避免大图
         */
        fun resizeLargeBitmap(bitmap: Bitmap, maxSize: Int): Bitmap {
            val width = bitmap.width
            val height = bitmap.height
            
            if (width <= maxSize && height <= maxSize) {
                return bitmap
            }
            
            val scale = Math.min(maxSize.toFloat() / width, 
                               maxSize.toFloat() / height)
            
            val newWidth = (width * scale).toInt()
            val newHeight = (height * scale).toInt()
            
            return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true)
        }
    }
}

六、内存泄漏检测

6.1 常见内存泄漏场景

6.1.1 静态引用泄漏

kotlin
// 错误示例:静态变量持有 Activity 引用
class BadSingleton {
    companion object {
        private lateinit var activity: Activity // 泄漏!
    }
    
    fun setActivity(activity: Activity) {
        this.activity = activity
    }
}

// 正确示例:使用弱引用
class GoodSingleton {
    companion object {
        private var activityRef: WeakReference<Activity>? = null
        
        val activity: Activity?
            get() = activityRef?.get()
    }
    
    fun setActivity(activity: Activity) {
        activityRef = WeakReference(activity)
    }
}

6.1.2 线程未注销

kotlin
// 错误示例:后台线程持有 Activity 引用
class BadAsyncTask : AsyncTask<Void, Void, Void>() {
    private lateinit var activity: Activity
    
    override fun doInBackground(vararg params: Void): Void {
        // 长时间运行
        Thread.sleep(10000)
        return null
    }
    
    override fun onPostExecute(result: Void) {
        // Activity 可能已经销毁
        activity.showToast("Done") // 泄漏!
    }
}

// 正确示例:使用弱引用
class GoodAsyncTask(activity: Activity) {
    private val activityRef = WeakReference(activity)
    
    fun execute() {
        Thread {
            // 长时间运行
            Thread.sleep(10000)
            
            val activity = activityRef.get()
            if (activity != null && !activity.isFinishing) {
                activity.runOnUiThread {
                    activity.showToast("Done")
                }
            }
        }.start()
    }
}

6.1.3 内部类泄漏

kotlin
// 错误示例:非静态内部类持有外部类引用
class LeakActivity : Activity() {
    class BadInnerClass {
        fun doSomething() {
            // 隐式持有 LeakActivity 的引用
            LeakActivity.log("Something")
        }
    }
}

// 正确示例:使用静态内部类
class LeakActivity : Activity() {
    companion object {
        fun log(message: String) {
            Log.d("LeakActivity", message)
        }
    }
    
    class GoodInnerClass(private val activityRef: WeakReference<Activity>) {
        fun doSomething() {
            val activity = activityRef.get() ?: return
            activity.runOnUiThread {
                LeakActivity.log("Something")
            }
        }
    }
}

6.1.4 资源未关闭

kotlin
// 错误示例:未关闭资源
class ResourceLeak {
    var cursor: Cursor? = null
    
    fun queryDatabase() {
        cursor = db.query(...) // 未关闭
    }
}

// 正确示例:及时关闭资源
class ResourceManaged {
    var cursor: Cursor? = null
    
    fun queryDatabase() {
        cursor = db.query(...)
        try {
            // 使用 cursor
            while (cursor?.moveToNext() == true) {
                // 处理数据
            }
        } finally {
            cursor?.close()
            cursor = null
        }
    }
}

6.1.5 监听器泄漏

kotlin
// 错误示例:未注销监听器
class BadActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        eventBus.register(this) // 注册监听器
        
        // 忘记注销
    }
    
    // 忘记在 onStop/onDestroy 中注销
    // override fun onDestroy() {
    //     eventBus.unregister(this)
    //     super.onDestroy()
    // }
}

// 正确示例:及时注销
class GoodActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        eventBus.register(this)
    }
    
    override fun onDestroy() {
        eventBus.unregister(this)
        super.onDestroy()
    }
}

6.2 LeakCanary 使用

6.2.1 集成配置

gradle
// build.gradle
dependencies {
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.14'
}
kotlin
// Application 类
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        
        if (BuildConfig.DEBUG) {
            LeakCanary.install(this)
        }
    }
}

6.2.2 自定义检测

kotlin
// 检测特定对象
class LeakDetector {
    fun detect(activity: Activity) {
        if (BuildConfig.DEBUG) {
            val refWatcher = RefWatcher.create()
            refWatcher.watch(activity, "Activity")
        }
    }
}

6.3 MAT 分析工具

6.3.1 生成 Hprof 文件

kotlin
// 手动生成 Hprof 文件
fun dumpHprof(context: Context, fileName: String = "heap.hprof") {
    val file = File(context.externalCacheDir, fileName)
    
    try {
        val fos = FileOutputStream(file)
        Debug.dumpHprofData(file.absolutePath)
        fos.close()
        
        Log.d("Hprof", "Dumped to: ${file.absolutePath}")
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

// 使用 ADB 命令获取
// adb shell dumpsys meminfo <package_name>
// adb shell run-as <package_name> dumpsys gc

6.3.2 分析泄漏路径

泄漏路径示例:

com.example.LeakedActivity @ 0x12345678
  ├─ activityThread: android.app.ActivityThread
  │   └─ views: android.view.View
  │       └─ mListeners: java.util.ArrayList
  │           └─ item: com.example.BadListener
  │               └─ activityRef: com.example.LeakedActivity  ← 泄漏点

6.4 Android Studio Profiler

6.4.1 内存分析步骤

kotlin
// 1. 启动应用并进入调试模式
// 2. 打开 Profiler (View -> Tool Windows -> Profiler)
// 3. 选择 Memory Profiler
// 4. 执行可能导致泄漏的操作
// 5. 点击 GC 按钮触发垃圾回收
// 6. 分析内存变化

// 关键指标:
// - Heap: 堆内存使用情况
// - Alloc: 内存分配速率
// - GC: 垃圾回收事件

6.4.2 查找泄漏对象

kotlin
// 使用 Profiler 的查找功能:
// 1. 点击 "Start Java Object Monitor"
// 2. 输入要监控的对象类名
// 3. 执行操作
// 4. 查看对象的引用链

七、内存优化技巧

7.1 代码级优化

7.1.1 对象复用

kotlin
// 优化前:每次创建新对象
fun processText(text: String): String {
    val sb = StringBuilder()
    for (char in text) {
        sb.append(char.uppercaseChar())
    }
    return sb.toString()
}

// 优化后:对象复用
class TextProcessor {
    private val sb = StringBuilder()
    
    fun processText(text: String): String {
        sb.clear()
        for (char in text) {
            sb.append(char.uppercaseChar())
        }
        return sb.toString()
    }
}

// 更好的方案:使用线程本地变量
class ThreadLocalTextProcessor {
    private val sbLocal = ThreadLocal { StringBuilder() }
    
    fun processText(text: String): String {
        val sb = sbLocal.get()
        sb.clear()
        for (char in text) {
            sb.append(char.uppercaseChar())
        }
        return sb.toString()
    }
}

7.1.2 减少临时对象

kotlin
// 优化前:大量临时对象
fun sumSquares(numbers: List<Int>): Int {
    return numbers
        .map { it * it }  // 创建中间集合
        .sum()
}

// 优化后:使用迭代
fun sumSquaresOptimized(numbers: List<Int>): Int {
    var sum = 0
    for (number in numbers) {
        sum += number * number
    }
    return sum
}

7.1.3 使用基本类型

kotlin
// 优化前:使用包装类型
fun calculate(ints: List<Int>): List<Int> {
    return ints.map { it * 2 }
}

// 优化后:使用基本类型数组
fun calculateOptimized(ints: IntArray): IntArray {
    val result = IntArray(ints.size)
    for (i in ints.indices) {
        result[i] = ints[i] * 2
    }
    return result
}

7.2 架构级优化

7.2.1 懒加载

kotlin
// 懒加载数据
class LazyDataLoader {
    private var data: List<Item>? = null
    
    val dataList: List<Item>
        get() {
            if (data == null) {
                data = loadFromDatabase()
            }
            return data!!
        }
    
    private fun loadFromDatabase(): List<Item> {
        // 加载数据
        return listOf()
    }
}

7.2.2 分页加载

kotlin
class PaginatedLoader {
    private val pageSize = 20
    private var currentPage = 0
    private val items = mutableListOf<Item>()
    
    fun loadMore(): List<Item> {
        val newItems = loadPage(currentPage, pageSize)
        items.addAll(newItems)
        currentPage++
        return newItems
    }
    
    fun releaseOldData() {
        // 只保留最近两页
        val keepCount = pageSize * 2
        if (items.size > keepCount) {
            items.subList(0, items.size - keepCount).clear()
        }
    }
    
    private fun loadPage(page: Int, size: Int): List<Item> {
        // 从数据源加载
        return listOf()
    }
}

7.2.3 缓存策略

kotlin
class SmartCache {
    private val memoryCache = LruCache<String, Any>(10 * 1024 * 1024)
    private val diskCache = DiskLruCache(50 * 1024 * 1024)
    
    fun get(key: String): Any? {
        // L1: 内存缓存
        var result = memoryCache.get(key)
        if (result != null) return result
        
        // L2: 磁盘缓存
        result = diskCache.read(key)
        if (result != null) {
            memoryCache.put(key, result)
        }
        
        return result
    }
    
    fun put(key: String, value: Any) {
        memoryCache.put(key, value)
        diskCache.write(key, value)
    }
}

7.3 配置优化

7.3.1 AndroidManifest 配置

xml
<!-- 开启大内存模式 -->
<application
    android:largeHeap="true"
    ... >
</application>

<!-- 配置内存警告接收器 -->
<receiver android:name=".MemoryWarningReceiver">
    <intent-filter>
        <action android:name="android.intent.action.MEMORY_USAGE_CHANGED" />
    </intent-filter>
</receiver>

7.3.2 gradle 配置

gradle
// 启用 R8 混淆优化
android {
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
        }
    }
}

7.4 工具链优化

7.4.1 内存监控工具

kotlin
class MemoryMonitor {
    companion object {
        private val TAG = "MemoryMonitor"
        
        fun startMonitoring() {
            Thread {
                while (true) {
                    logMemoryUsage()
                    Thread.sleep(1000)
                }
            }.start()
        }
        
        private fun logMemoryUsage() {
            val runtime = Runtime.getRuntime()
            val used = (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024
            val total = runtime.totalMemory() / 1024 / 1024
            val max = runtime.maxMemory() / 1024 / 1024
            
            Log.d(TAG, "Memory: ${used}/${total}/${max} MB")
        }
    }
}

7.4.2 性能测试

kotlin
class MemoryBenchmark {
    fun runBenchmark() {
        // 1. 清除缓存
        Runtime.getRuntime().gc()
        
        // 2. 记录初始内存
        val initialMemory = getMemoryUsage()
        
        // 3. 执行测试操作
        performOperations()
        
        // 4. 触发 GC
        Runtime.getRuntime().gc()
        Thread.sleep(100)
        Runtime.getRuntime().gc()
        
        // 5. 记录最终内存
        val finalMemory = getMemoryUsage()
        
        // 6. 计算内存增量
        val memoryDelta = finalMemory - initialMemory
        Log.d("Benchmark", "Memory delta: $memoryDelta KB")
    }
    
    private fun getMemoryUsage(): Long {
        val runtime = Runtime.getRuntime()
        return runtime.totalMemory() - runtime.freeMemory()
    }
    
    private fun performOperations() {
        // 测试代码
    }
}

八、面试考点

8.1 基础考点

8.1.1 概念理解

Q1: 解释 Android 内存模型

答案要点:
1. 虚拟内存与物理内存的区别
2. 进程地址空间划分(堆、栈、共享库等)
3. Java 堆和 Native 堆的区别
4. ART 的分代内存管理

Q2: 什么是 LRU 算法?

答案要点:
1. Least Recently Used(最近最少使用)
2. 核心思想:淘汰最久未使用的数据
3. 实现:LinkedHashMap + 双向链表
4. 时间复杂度:O(1)

Q3: Bitmap 内存如何计算?

答案要点:
内存 = 宽 × 高 × 每个像素的字节数
ARGB_8888: 4 字节/像素
RGB_565: 2 字节/像素
ALPHA_8: 1 字节/像素

8.1.2 代码分析

Q4: 以下代码有什么内存问题?

kotlin
class BadActivity : Activity() {
    private val imageCache = mutableMapOf<String, Bitmap>()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        for (i in 1..100) {
            val bitmap = BitmapFactory.decodeResource(resources, R.drawable.img)
            imageCache[i.toString()] = bitmap
        }
    }
    
    // 忘记释放
}
问题分析:
1. Bitmap 未释放,导致内存泄漏
2. 没有使用 LruCache,可能导致 OOM
3. Activity 销毁后,Map 仍持有 Bitmap 引用

正确做法:
1. 使用 LruCache 管理 Bitmap
2. 在 onDestroy 中清除缓存
3. 使用 WeakReference 或 SoftReference

8.2 进阶考点

8.2.1 场景设计

Q5: 如何设计一个图片加载库的内存缓存?

答案要点:
1. 使用 LruCache 作为内存缓存基础
2. 根据设备内存动态设置缓存大小
3. 支持图片尺寸缩放,避免加载大图
4. 使用内存池复用 Bitmap 对象
5. 支持缓存过期时间
6. 支持取消正在加载的任务
7. 双缓存策略(内存 + 磁盘)

Q6: 如何处理大图导致的 OOM?

答案要点:
1. 使用 inSampleSize 缩放加载
2. 使用 inJustDecodeBounds 先获取图片信息
3. 使用 BitmapRegionDecoder 加载部分区域
4. 使用 LruCache 限制缓存大小
5. 考虑使用第三方库(Glide、Picasso)
6. 监控内存使用情况

8.3 高级考点

8.3.1 深度分析

Q7: ART 的 GC 机制是怎样的?

答案要点:
1. 分代 GC 策略
   - 新生代:复制算法
   - 老年代:标记 - 清理/整理算法

2. GC 类型
   - Concurrent Copying GC
   - Concurrent Mark Compact GC
   - Background Mark Compact GC

3. GC 触发条件
   - 内存不足
   - 分配失败
   - 手动请求

4. Stop-The-World 优化
   - 并发 GC 减少停顿
   - 后台 GC 完全并发

Q8: 如何分析内存泄漏?

答案要点:
1. 使用工具
   - LeakCanary(开发期)
   - Android Studio Profiler(调试期)
   - MAT(生产期)

2. 分析步骤
   - 生成 Hprof 文件
   - 查找支配树(Dominators)
   - 分析引用链(Reference Chain)
   - 定位泄漏点

3. 常见泄漏类型
   - 静态引用
   - 未注销监听器
   - 后台线程未停止
   - 内部类引用外部类

8.3.2 性能优化

Q9: 如何优化应用内存占用?

答案要点:
1. 代码层面
   - 对象复用
   - 减少临时对象创建
   - 使用基本类型
   - 及时释放资源

2. 架构层面
   - 懒加载
   - 分页加载
   - 合理的缓存策略
   - 异步加载

3. 配置层面
   - 优化资源文件
   - 使用 WebP 格式
   - 启用 ProGuard/R8

4. 工具层面
   - 定期内存分析
   - 性能测试
   - 持续监控

Q10: LruCache 和 WeakReference 有什么区别?

答案要点:
1. 管理机制
   - LruCache: 主动管理,基于容量淘汰
   - WeakReference: 被动管理,由 GC 决定

2. 使用场景
   - LruCache: 缓存热点数据
   - WeakReference: 不强制持有对象

3. 性能
   - LruCache: O(1) 访问,可控
   - WeakReference: 不确定回收时机

4. 最佳实践
   - 结合使用:LruCache + WeakReference
   - 根据场景选择

九、实战案例

9.1 图片加载器实现

kotlin
class SimpleImageLoader(private val context: Context) {
    
    private val memoryCache: LruCache<String, Bitmap>
    private val diskCache: DiskLruCache
    private val tasks = ConcurrentHashMap<String, AsyncTask<*, *, Bitmap>>()
    
    init {
        // 内存缓存
        val maxMemory = Runtime.getRuntime().maxMemory() / 8
        memoryCache = object : LruCache<String, Bitmap>(maxMemory.toInt()) {
            override fun sizeOf(key: String, value: Bitmap): Int {
                return value.byteCount
            }
            
            override fun entryRemoved(
                evicted: Boolean,
                key: String,
                oldValue: Bitmap,
                newValue: Bitmap?
            ) {
                if (evicted && !oldValue.isRecycled) {
                    oldValue.recycle()
                }
            }
        }
        
        // 磁盘缓存
        diskCache = DiskLruCache.open(
            context.cacheDir.resolve("images"),
            1, 1, 50 * 1024 * 1024
        )
    }
    
    fun loadBitmap(
        url: String,
        imageView: ImageView,
        maxWidth: Int = 0,
        maxHeight: Int = 0
    ) {
        // 取消之前的任务
        tasks[url]?.cancel(true)
        
        // 检查内存缓存
        val cachedBitmap = memoryCache.get(url)
        if (cachedBitmap != null) {
            imageView.setImageBitmap(cachedBitmap)
            return
        }
        
        // 创建加载任务
        val task = object : AsyncTask<*>() {
            override fun doInBackground(vararg params: Any?): Bitmap {
                // 检查磁盘缓存
                var bitmap = diskCache.read(url)
                
                if (bitmap == null) {
                    // 从网络加载
                    bitmap = loadImageFromNetwork(url)
                    
                    // 保存到磁盘
                    diskCache.write(url, bitmap)
                }
                
                // 缩放
                return scaleBitmap(bitmap, maxWidth, maxHeight)
            }
            
            override fun onPostExecute(bitmap: Bitmap) {
                // 检查任务是否被取消
                if (task.isCancelled) return
                
                // 保存到内存缓存
                memoryCache.put(url, bitmap)
                
                // 设置图片
                imageView.setImageBitmap(bitmap)
            }
        }
        
        tasks[url] = task
        task.execute()
    }
    
    private fun loadImageFromNetwork(url: String): Bitmap {
        // 实现网络加载
        return Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
    }
    
    private fun scaleBitmap(
        bitmap: Bitmap,
        maxWidth: Int,
        maxHeight: Int
    ): Bitmap {
        if (maxWidth == 0 && maxHeight == 0) return bitmap
        
        val width = bitmap.width
        val height = bitmap.height
        
        val scale = when {
            maxWidth == 0 -> maxHeight.toFloat() / height
            maxHeight == 0 -> maxWidth.toFloat() / width
            else -> minOf(
                maxWidth.toFloat() / width,
                maxHeight.toFloat() / height
            )
        }
        
        return Bitmap.createScaledBitmap(
            bitmap,
            (width * scale).toInt(),
            (height * scale).toInt(),
            true
        )
    }
    
    fun clearCache() {
        memoryCache.evictAll()
        diskCache.clear()
    }
}

9.2 内存监控组件

kotlin
class MemoryMonitorService : Service() {
    
    private val handler = Handler(Looper.getMainLooper())
    private val memoryThreshold = 0.8 // 80%
    private var isMonitoring = false
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        startMonitoring()
        return START_STICKY
    }
    
    override fun onBind(intent: Intent?): IBinder? = null
    
    private fun startMonitoring() {
        if (isMonitoring) return
        
        isMonitoring = true
        handler.postDelayed(MonitorRunnable(), 1000)
    }
    
    private inner class MonitorRunnable : Runnable {
        override fun run() {
            checkMemoryUsage()
            handler.postDelayed(this, 1000)
        }
    }
    
    private fun checkMemoryUsage() {
        val runtime = Runtime.getRuntime()
        val used = runtime.totalMemory() - runtime.freeMemory()
        val max = runtime.maxMemory()
        val usage = used.toDouble() / max
        
        if (usage > memoryThreshold) {
            handleHighMemory()
        }
    }
    
    private fun handleHighMemory() {
        // 释放内存
        releaseMemory()
        
        // 通知用户
        sendNotification()
    }
    
    private fun releaseMemory() {
        // 清除缓存
        // 释放 Bitmap
        // 停止后台任务
    }
    
    private fun sendNotification() {
        // 发送通知
    }
    
    override fun onDestroy() {
        isMonitoring = false
        handler.removeCallbacksAndMessages(null)
        super.onDestroy()
    }
}

十、总结与最佳实践

10.1 核心要点

  1. 理解内存模型:掌握 Android 内存区域划分和 ART 内存管理机制
  2. 合理使用缓存:根据场景选择 LruCache、WeakReference 等方案
  3. 优化 Bitmap 加载:使用合适的尺寸和配置,及时释放
  4. 检测内存泄漏:使用 LeakCanary、Profiler 等工具
  5. 持续优化:定期分析内存使用情况,持续优化代码

10.2 检查清单

kotlin
// 内存优化检查清单
class MemoryOptimizationChecklist {
    companion object {
        // 1. 缓存管理
        const val CHECK_CACHE_SIZE = "是否设置了合理的缓存大小?"
        const val CHECK_CACHE_POLICY = "是否使用了合适的缓存淘汰策略?"
        
        // 2. Bitmap 管理
        const val CHECK_BITMAP_SIZE = "是否使用了合适的 Bitmap 尺寸?"
        const val CHECK_BITMAP_CONFIG = "是否选择了合适的 Bitmap 配置?"
        const val CHECK_BITMAP_RELEASE = "是否及时释放了 Bitmap?"
        
        // 3. 内存泄漏
        const val CHECK_STATIC_REF = "是否存在静态引用导致的泄漏?"
        const val CHECK_LISTENER = "是否及时注销了监听器?"
        const val CHECK_THREAD = "后台线程是否正确停止?"
        
        // 4. 资源管理
        const val CHECK_CURSOR = "是否关闭了 Cursor?"
        const val CHECK_STREAM = "是否关闭了 InputStream/OutputStream?"
        
        // 5. 性能优化
        const val CHECK_OBJECT_REUSE = "是否复用了对象?"
        const val CHECK_TEMP_OBJECT = "是否减少了临时对象创建?"
    }
}

10.3 推荐资源

  • 官方文档

    • Android Developers: Memory Management
    • ART 源码分析
  • 工具

    • LeakCanary
    • Android Studio Profiler
    • MAT (Memory Analyzer Tool)
  • 书籍

    • 《Android 性能优化实战》
    • 《深入理解 Android 虚拟机》

附录

A. 常用内存单位换算

1 KB = 1024 Bytes
1 MB = 1024 KB = 1,048,576 Bytes
1 GB = 1024 MB = 1,073,741,824 Bytes

B. 常见 Bitmap 配置

配置字节/像素说明
ALPHA_81只有透明度
RGB_5652无透明度,16 位色
ARGB_44442有透明度,低质量
ARGB_88884有透明度,高质量

C. 内存错误代码

错误含义解决方案
OOM内存溢出减少内存使用,优化缓存
FATAL EXCEPTION: java.lang.OutOfMemoryError严重 OOM检查 Bitmap 加载和缓存策略
Allocation failed分配失败检查可用内存,触发 GC

本文档持续更新,欢迎贡献和反馈

版本: 1.0
作者: Android 面试指南团队
最后更新: 2024 年