# flutter_memory_lab **Repository Path**: TTGF/flutter_memory_lab ## Basic Information - **Project Name**: flutter_memory_lab - **Description**: Flutter 内存管理实验室:GC、泄漏、生命周期、图片缓存与 DevTools 排查示例 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-31 - **Last Updated**: 2026-05-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Flutter 内存管理实验室 一个可以配合 Flutter DevTools 使用的内存管理 Demo,用于观察常见内存问题、对象保留和资源释放方式。 ## 运行环境 ```text Flutter SDK:3.41.6 stable Dart SDK:3.11.4 DevTools:2.54.2 ``` ## 实验场景 - GC 回收验证:使用强引用、`WeakReference` 和 `Finalizer` 观察对象回收条件。 - 内存峰值与泄漏:区分临时内存上涨和被强引用长期持有的真正泄漏。 - 控制器释放:对比页面退出时 `dispose` 与全局集合长期持有控制器。 - 定时器与订阅:观察周期定时器未取消时的保留状态。 - 闭包持有页面引用:模拟全局闭包捕获 `BuildContext`。 - 页面生命周期:反复进入和退出页面,观察 `initState`、`dispose` 与资源释放计数。 - 图片缓存:加载图片并观察 `ImageCache` 指标,支持主动清理缓存。 - 图片像素内存估算:通过 `宽 × 高 × 4` 理解解码后像素内存。 - 长列表构建方式:对比 `ListView.builder` 与一次性创建大量 Widget。 ## 运行 ```bash flutter pub get flutter run ``` 图片缓存实验默认使用网络图片,也可以主动切换到本地图片。网络请求失败时会自动使用本地资源兜底。 首页按照三个层级组织实验: - 基础原理:理解对象可达性、GC、临时峰值和真正泄漏。 - 常见泄漏:观察控制器、定时器、闭包和页面生命周期。 - 高内存场景:分析图片缓存、像素解码内存和列表构建方式。 首页的“释放全部实验对象”会统一清理控制器、定时器、闭包、持续占用内存、GC 实验记录、生命周期计数和图片缓存。 ## GC 回收验证原理 `GC 回收验证` 页面会创建带有内存载荷的实验对象,并同时保存: - 强引用:对象仍然可达,GC 不会回收。 - `WeakReference`:可以观察对象是否仍然存活,但不会阻止回收。 - `Finalizer`:对象被 GC 回收后,异步记录回调结果。 推荐先保留强引用并在 DevTools 中触发 GC,再移除强引用并再次触发 GC。需要注意,应用代码无法精确控制 GC 的执行时机,`Finalizer` 回调也不是同步发生的。 ## 建议学习顺序 1. GC 回收验证:先理解“可达”和“不可达”。 2. 内存峰值与泄漏:区分短暂上涨、缓存和真正泄漏。 3. 页面生命周期:理解 `dispose` 与 GC 的职责差异。 4. 控制器、定时器、闭包:观察业务代码里最常见的保留路径。 5. 图片缓存与像素内存:理解图片为什么容易造成高内存。 6. 长列表构建方式:观察 Widget 创建策略对内存的影响。 ## 核心原理 - GC 负责回收不可达的 Dart 对象。 - `dispose` 负责主动释放控制器、订阅、定时器等资源。 - 解除强引用后,对象只是具备被回收条件,不代表立刻回收。 - 内存上涨不等于内存泄漏。临时对象、缓存和持续泄漏需要分别判断。 - 图片文件大小不等于解码后的像素内存。像素内存可以粗略按 `宽 × 高 × 4` 估算。 ## 使用 DevTools 观察 1. 使用调试模式启动 App。 2. 打开 Flutter DevTools 的 Memory 页面。 3. 进入某个实验页,点击制造问题按钮。 4. 返回首页,观察当前保留对象数量。 5. 在 DevTools 中触发 GC,比较对象数量和内存曲线。 6. 点击释放按钮或首页的“释放全部实验对象”。 7. 再次触发 GC,观察对象是否可以回收。 ## 使用 Snapshot 排查泄漏 建议按照下面的顺序排查: 1. 进入实验页之前,在 DevTools Memory 页面拍摄 `Snapshot A`。 2. 进入实验页,点击制造问题按钮,然后退出页面。 3. 手动触发 GC,再拍摄 `Snapshot B`。 4. 使用 Diff 对比对象数量变化。 5. 按类名筛选,例如 `TextEditingController`、`Timer` 或自定义实验对象。 6. 查看对象的 Retaining Path,定位是谁仍然持有对象。 7. 清除引用或执行 `dispose`,再次触发 GC。 8. 拍摄 `Snapshot C`,确认对象数量是否下降。 通过 `Snapshot A -> Snapshot B -> Snapshot C`,可以把“对象新增”“无法回收”“修复后释放”三个阶段串起来。 ## 验证 ```bash flutter analyze flutter test ```