从渲染原理出发探究Flutter内存泄漏(超详细)


背景
众所周知 , 内存的高低是评判一款app的性能优劣的重要的指标之一 。 作为开发者而言 , 都会尽可能的减少内存的使用 , 清除无用的内存块 , 从而减少整个app的内存使用量 。 这也是历来开发者是追求的目标 。 然而 , 开发者难免时常因为语言用法或者写法的缘故 , 导致该释放而未释放的对象迟迟未释放 , 从而内存泄漏 , 消耗殆尽内存空间 , 从而导致系统崩溃的情况 。
如何更简单的帮助开发者分析、暴露且解决内存泄漏问题 , 几乎是每一个平台或框架、开发者亟需的一个的''标配''的feature 。 例如:苹果的instruments , Linux的Kmemleak等 。 但是对于flutter社区 , 缺少一款用得顺手的内存泄漏工具 。 对于使用flutter而言 , 因使用dart语言 , 通过形成渲染树提交到c++的skia进行渲染 , 从dart层到c++层拥有很长的渲染链路 , 使用者必须对整个渲染链路有通盘深刻的理解 , 才能深刻此时此刻的内存使用情况 。
本文从flutter渲染原理出发 , 分析flutter的内存分配情况 , 理清渲染流程 , 提出一种基于渲染树个数的方式寻找内存泄漏的解决方案
flutter内存都包含哪些
虚拟内存还是物理内存?
当我们谈论内存时 , 通常说的是物理内存(Physical memory) , 同一个应用程序运行在不同机器或者操作系统上时 , 会因不同操作系统和机器的硬件条件的不同 , 分配的到物理内存大小会有所不同 , 但大致而言 , 一款应用程序所使用到的虚拟内存(Virtual Memory)而言便会大致一样 , 本文讨论的都指的是虚拟内存 。
我们可以直观的理解 , 代码中操作的所有对象都是能用虚拟内存衡量 , 而不太关心对象是否存在于物理内存与否 , 只要能减少对象的应用 , 尽量少的持有对象 , 不管白猫黑猫 , 能减少对象的 , 都是“好猫” 。
讨论flutter内存时 , 我们在谈论什么
flutter从使用的语言上 , 可以分成3大部分 ,

  • Framework层 由Dart编写 , 开发者接触到顶层 , 用于应用层开发
  • Engine 层 , 由C/C++编写 , 主要进行图形渲染
  • Embedder层 , 由植入层语言编写 , 如iOS使用Objective-C/swift , Android使用java
当我们从进程角度谈论flutter应用的内存时 , 指的是这个三者所有的内存的总和 。
为简化 , 这里可以简单的以使用者能直接接触的代码为边界 , 将其分成DartVM和native内存 , DartVM指Dart虚拟机占用内存 , 而native内存包含Engine和平台相关的代码运行的内存 。
从渲染原理出发探究Flutter内存泄漏(超详细)
本文插图
既然说Flutter的使用者能接触到的最直接的对象都是使用Dart语言生成的对象 , 那么对于Engine层的对象的创建与销毁 , 使用者似乎鞭长莫及了?这就不得不说Dart虚拟机绑定层的设计了 。
Dart绑定层如何工作
出于性能或者跨平台或其他原因 , 脚本语言或者基于虚拟机的语言都会提供c/c++或函数对象绑定到具体语言对象的接口 , 以便在语言中接着操控c/c++对象或函数 , 这层API称为绑定层 。 例如: 最易嵌入应用程序中的Lua binding, Javascript V8 引擎的binding 等等 。
Dart虚拟机在初始化时 , 会将C++声明的某个类或者函数和某个函数和Dart中的某个类或者绑定起来 , 依次注入Dart运行时的全局遍历中 , 当Dart代码执行某一个函数时 , 便是指向具体的C++对象或者函数 。
下面是几个常见的绑定的几个c++类和对应的Dart类
flutter::EngineLayer --> ui.EngineLayer
flutter::FrameInfo --> ui.FrameInfo
flutter::CanvasImage --> ui.Image
flutter::SceneBuilder --> ui.SceneBuilder
flutter::Scene --> ui.Scene


推荐阅读