核心问题
对象什么时候会被回收?
理解这个问题,就理解了内存管理的核心。
知识脉络
第一层:JVM 内存结构
┌─────────────────────────────────────────┐
│ JVM 内存 │
├──────────────┬──────────────────────────┤
│ 新生代 │ 老年代 │
│ (Young Gen) │ (Old Gen) │
├─────┬───┬────┤ │
│Eden │S0 │ S1 │ │
└─────┴───┴────┴──────────────────────────┘
8 1 1 (默认比例)
对象晋升
- 新对象 → Eden
- Minor GC → 存活对象 → Survivor
- 年龄 ≥ 15 → Old Gen
第二层:垃圾回收
可达性分析
从 GC Roots 出发,遍历引用链,不可达的对象可回收。
GC Roots:
- 栈中引用
- 静态变量
- JNI 引用
GC 算法
| 算法 | 优点 | 缺点 |
|---|---|---|
| 标记-清除 | 简单 | 碎片 |
| 复制 | 无碎片 | 内存利用率低 |
| 标记-整理 | 无碎片 | 整理耗时 |
Android GC (ART)
- 前台 GC:部分暂停,用户可见
- 后台 GC:完整清理
第三层:内存泄漏
常见场景
| 场景 | 原因 | 解决 |
|---|---|---|
| 静态变量持有 Context | 生命周期不匹配 | Application Context |
| 内部类 | 隐式持有外部类 | 静态内部类 + 弱引用 |
| Handler | Message 队列持有 | onDestroy 清理 |
| 注册未取消 | 观察者未注销 | 及时 unregister |
检测工具
- LeakCanary:自动检测
- Memory Profiler:Heap Dump
- MAT:分析 hprof
面试高频点
Q1: 四种引用类型?
| 类型 | 回收时机 | 用途 |
|---|---|---|
| 强引用 | 永不 | 普通 |
| 软引用 | 内存不足 | 缓存 |
| 弱引用 | 下次 GC | 避免泄漏 |
| 虚引用 | 随时 | 跟踪回收 |
Q2: 内存抖动是什么?
频繁创建/销毁对象导致频繁 GC,表现为卡顿。
解决:对象复用、避免循环内创建。
Q3: OOM 排查步骤?
- Memory Profiler 查看内存分布
- 导出 hprof 分析大对象
- 检查 Bitmap、List 等大内存使用
- LeakCanary 检测泄漏