定位Flutter内存问题很难么?

内存水位升高导致的稳定性问题严重影响App用户体验,所以开发者们非常关注Flutter的内存表现 。随着Flutter业务越来越多,闲鱼也面临着oom导致的crash率提升的问题,下面我们结合项目中实际遇到的内存问题和解决思路跟大家分享下flutter内存优化的经验 。
本文分为三个部分:

  1. 了解Dart VM内存分配及销毁原理
  2. 通过Observatory工具分析内存泄漏,减少不必要的内存占用
  3. Flutter中常见内存泄漏场景有哪些,如何在业务应用中避免踩坑
 
Dart VM内存分配及销毁原理DartVM的垃圾回收机制分两个阶段,新生代(New Generation)和老年代(Old Generation) 。
新生代用来存储生命周期较短的对象,由两个内存空间组成,Active内存空间用来分配新对象,inActive内存空间用来作为备用空间,DartVM的内存分配策略非常简单,创建对象时只需要在现有堆上移动指针,内存增长始终是线形的,省去了查找可用内存段的过程 。每个Isolate有自己独立的Heap,相互之间无法共享内存,这样可以实现无锁的快速分配 。
一旦Active的内存空间被填满,垃圾回收器会从根对象开始遍历检查检查所有对象的引用状态,没有被引用的对象标记为dead状态,非dead状态的对象在下次内存回收事件中会被复制到inActive内存空间,清除Active内存空间,最后Active和inActive内存空间状态调换 。
定位Flutter内存问题很难么?

文章插图
当对象达到一定的生命周期,会被移到老年代内存空间管理,这种垃圾收集策略有两个阶段
  1. 首先遍历对象图,并标记仍在使用的对象 。
  2. 扫描整个内存,并回收任何未标记的对象,然后清除所有标志 。
这种内存清理的频率较低,并且在扫描回收阶段需要暂停Dart runtime,回收成本较高,比较适合Flutter中大量StatelessWidget的模式(大部分都存放在新生代) 。
定位Flutter内存问题很难么?

文章插图
另外,当engine检测到应用是idle状态并且没有用户交互的时候会发送通知垃圾收集器开始清理内存,最小化对性能的影响 。这些策略让Dart的内存分配和回收都非常高效 。
Android和IOS中都存在强引用弱引用的概念,区别在于一个对象具有强/弱引用,系统会不会释放该对象占用的内存空间,Dart并没有弱引用的概念,但是有个特例Expando,它会以弱引用的方式持有 key,相当于一个弱应用的map,感兴趣的可以了解下 。
Dart VM借鉴了很多JVM的思路,Dart中产生内存泄露的方式也和JAVA类似,Java中很多排查内存泄露的思路和防止内存泄露的方法应该也可以借鉴过来 。Android可以通过Profile和LeakMemory等工具检测app中的内存泄漏,Flutter如何检测呢?可以使用Observatory或者DevTools 。
 
通过Observatory分析内存泄漏Observatory是官方提供的调试工具,通过dart vm获取运行时信息,通过它我们可以分析一系列性能相关数据,例如app耗时统计,代码覆盖率等,这里我们重点介绍内存相关的调试工具 。(DevTools也可以用来调试分析性能数据,它是在Observatory层做了一层封装,但是目前还是beta版本) 。
下面我们用闲鱼中的实际例子介绍下如何使用Observatory检查看Dart VM内存使用情况,注意所有关于性能的分析要在Profile模式下进行 。
  • 打开Observatory URL的Web页面 。运行app,在控制台中查找类似输出日志 listening on ws://127.0.0.1:64673/hXsWR_ZOsGk=/ws,表示当前连接的VM地址,输入到浏览器就可以看到Observatory主界面,显示了dart vm一些基础信息,具体使用方法可以参考 官方文档,这里不再详细描述,我们重点关注右下角的allocation profile选项 。

定位Flutter内存问题很难么?

文章插图
  • 点击右下角allocation profile选项后,操作app进入想要分析的Flutter页操作,退出该页面,点击页面右上角 GC按钮触发手动GC,查看Class,发现有部分DX Class内存占用,这类class本应该只有在目标分析页会出现,退出目标分析也后手动GC会被完全释放,但是这里任然能看到相关内存占用,说明产生了内存泄漏 。

定位Flutter内存问题很难么?

文章插图