分享好友 最新动态首页 最新动态分类 切换频道
Flutter3.0 性能优化系列|一文教你完全掌握内存优化和dark虚拟机 (内存泄露)
2024-12-26 13:13

flutter从使用的语言上,可以分成3大部分

Flutter3.0 性能优化系列|一文教你完全掌握内存优化和dark虚拟机 (内存泄露)

  • Framework层 由Dart编写,开发者接触到顶层,用于应用层开发
  • Engine 层,由C/C++编写,主要进行图形渲染
  • Embedder层,由植入层语言编写,如iOS使用Objective-C/swift,Android使用java

当我们从进程角度谈论flutter应用的内存时,指的是这个三者所有的内存的总和。

Dart虚拟机的理解:

在Flutter中,内存管理是由Dart虚拟机负责的。Dart虚拟机使用垃圾回收器来管理内存,这意味着开发者不需要手动分配和释放内存。垃圾回收器会自动识别不再使用的对象并将其回收,从而释放内存。

Flutter应用程序中的内存分为两种类型:堆内存和栈内存。堆内存用于存储对象,栈内存用于存储临时变量。在Flutter中,大多数对象都是在堆内存中创建的,而临时变量则是在栈内存中创建的。当一个对象不再被引用时,垃圾回收器会自动回收它所占用的堆内存。

Dart虚拟机是一个支持JIT编译和AOT编译的强类型脚本语言虚拟机,它可以运行在多种平台上,包括PC、移动设备和嵌入式设备上。Dart虚拟机内置了大量的标准库和工具,包括Dart SDK、Dart Analyzer、Dartfmt等,方便开发者进行开发和维护。Dart虚拟机支持使用各种IDE,包括Flutter、IntelliJ IDEA等,提供了强大的代码编辑和调试功能,大大提高了开发效率。

Dart虚拟机的设计架构具有良好的可扩展性和可定制性,支持诸如Dart的Native扩展等机制,可以在运行时加载和卸载模块进行动态扩展。Dart虚拟机还支持一些高级语言特性,如异步编程和生成器,使得开发者可以编写更加高效的、带有处理异步逻辑的程序。同时,Dart虚拟机还支持代码优化和预编译等特性,以提高程序的性能和运行效率。

总之,Dart虚拟机是一个具有很多优良特性的强类型脚本语言虚拟机,与Flutter等生态系统紧密结合,为移动应用开发提供了一个全面、高效的解决方案。

重点:

Dart虚拟机在初始化时,会将C++声明的某个类或者函数和某个函数和Dart中的某个类或者绑定起来,依次注入Dart运行时的全局遍历中,当Dart代码执行某一个函数时,便是指向具体的C++对象或者函数。

2.1 flutter编译模式

Flutter支持Release、Profile、Debug编译3种模式。

  1. Release模式,使用AOT预编译模式,预编译为机器码,通过编译生成对应架构的代码,在用户设备上直接运行对应的机器码,运行速度快,执行性能好;此模式关闭了所有调试工具,只支持真机。
  2. Profile模式,和Release模式类似,使用AOT预编译模式,此模式最重要的作用是可以用DevTools来检测应用的性能,做性能调试分析。
  3. Debug模式,使用JIT(Just in time)即时编译技术,支持常用的开发调试功能hot reload,在开发调试时使用,包括支持的调试信息、服务扩展、Observatory、DevTools等调试工具,支持模拟器和真机。

2.2 开启profile模式

Android-Android Studio 开发工具来查看 APP 内存占用; 按照 Flutter 官方的性能优化指南,**Flutter-profile 模式下,使用 devTools **查看 Flutter 内存占用

图片过载检测:切换到 Flutter Inspector,点击 Highlight Oversized Images

2.3 、检测消耗多余内存的图片

Flutter Inspector:点击 “Highlight Oversizeded Images”,它会识别出那些解码大小超过展示大小的图片,并且系统会将其倒置,这些你就能更容易在 App 页面中找到它。

3. 1 内存泄露原理:(重点)

从渲染原理出发探究Flutter内存泄漏

3.1.1案例1:

例如,存在两个A,B界面,A界面通过Navigator.push的方式添加B界面,B界面通过Navigator.pop回退到A。 如果B界面因为某些写法的缘故导致B的渲染树虽然被从主渲染树解开后依然无法被释放,这会导致整个原来B的子树都无法释放

3.1.2 内存泄工具设计原理

flutter内存泄漏检测工具的设计思路是: 借鉴Android LeakCanary,

用WeakPersitentHandle对象, 通过检测渲染树节点来检测内存泄漏

对比界面进入前后的对象,寻找出未被释放的对象,进而查看未释放的引用关系(Retaining path或Inbound references,再结合源码进行分析,最后找到错误代码。

使用Flutter自带的Observatory纵然可以一个一个查看每个泄漏对象的引用关系,但是对于一个稍微复杂一点的界面而言,最终生成的layer个数是非常庞杂的,想要在Observatory所有的泄漏对象中找到有问题的代码是一项非常庞杂的任务。

为此我们将这些繁杂的定位工作都进行了可视化。

我们这里将每一帧提交到engine的所有EngineLayer进行了一个记录,并且以折线图的形式记录下来,如果上文说的内存中的layer个数异常的大于使用中的layer个数,那么就可判断前一个页面存在有内存泄漏。

Dart代码中都是通过往ui.SceneBuilder添加EngineLayer的方式去构建渲染树,那么我们只要检测c++中内存中EngineLayer的个数,对比当前帧使用的EngineLayer个数,如果内存中的EngineLayer个数长时间大于使用的个数,那么我们可以判断存在有内存泄漏

内存泄露检测原理总结:

1). dart代码都会映射c++中对应的方法

2). 对象存放在 WeakPersitentHandle中

3). 根据渲染原理: 通过检测渲染树节点数量来检测内存泄漏, 对比当前帧使用的EngineLayer个数,如果内存中的EngineLayer个数长时间大于使用的个数

4). 通过WeakPersitentHandle找到调用链

内存泄露工具: (重点分析)

内存泄露具体代码总结:

1. 弱引用Expando

2. 解决访问的私有属性Dart vm_service

3. 判断内存泄露

先通过 vm_service 获取到 ,遍历里面的 属性,找到 字段(注意 是 ObjRef 类型,用同样的办法把 字段转成 类型(_data 是个数组,Obj 里面有数组的 child 信息)。

遍历 字段,如果都是 null,表明我们观测的 key 对象已经被释放了。如果 item 不为 null,再次把 item 转为 对象,取它的

4. 手动调用GC

5.获取泄漏路径

6. 优化泄漏路径, 得到最短泄漏路径, 通过聚合和分类进行优化

3.2 内存泄漏的常见场景
案例:

目前发现异步执行的代码的场景(Feature, async/await,methodChan)长期持有传入的BuildContext,导致 element 被移除后,依然长期存在,最终导致以及关联的 widget, state 发生泄漏。

3.2.1. 监听反注册缺失

排查内存泄漏的过程中,我们发现图片内存大幅度超出了图片缓存自身 size 限制的增长,并且不会被 GC 回收,经过排查发现我们封装的一个底层图片处理类,注册了图片事件流监听后,并没有在适当的时机做反注册处理。

3.2.2. 长列表直接构建列表项

通过对列表数据遍历的方式,一次生成所有数据对应的 widget 列表,直接塞进 Column 里展示给用户,当加载了几页数据之后,数据量稍大就会轻易导致 OOM 或导致严重卡顿。

3.2.3. 延时、持续执行的闭包引用

Flutter 提供的延时和持续执行的对象有 Animation、Timer、Future 等,在结束执行之前,回调函数引用到的相关对象都会被强引用保留在内存中

3.3 内存泄漏的操作步骤和实战: (重点)

通过Observatory分析内存泄漏

Android Studio 或 VS Code 插件帮我们包装了相关内存工具,这些工具都基于 debug 模式下 Dart VM service 暴露的接口开发的,Dart VM service 自身也带有协助排查内存问题的工具 - Dart VM Observatory,attach 之后访问 service 提供的 http 链接即可使用该工具,内存排查功能路径为:Isolate (main) --> Allocation Profile。

工具有了,如何排查定位导致内存泄漏的问题代码呢

对同一个功能或页面进行反复相同的进入、退出操作

然后执行强制 GC,查看不同操作后的内存快照

对比该功能关联的对象实例增加情况

如果强制 GC 后实例只增不减或该回收的对象没有被回收,没有特殊的延时处理一般就可以判断相关代码有问题

3.3 图片和listview优化

重绘情况,酌情考虑优化方案(**控制 setState 粒度、**引入全局状态管理-redux/Provider、拆分 ViewModel、减少 build() 操作 等

针对这些图片,你可以指定 cacheWidth 和 cacheHeight 为展示大小,这样可以让 flutter 引擎以指定大小解析图片,减少内存消耗。

针对 ListView item 中有 image 的情况来优化内存

ListView 不会销毁那些在屏幕可视范围之外的那些 item,如果 item 使用了高分辨率的图片,那么它将会消耗非常多的内存。

ListView 在默认情况下会在整个滑动/不滑动的过程中让子 Widget 保持活动状态,这一点是通过 AutomaticKeepAlive 来保证,在默认情况下,每个子 Widget 都会被这个 Widget 包裹,以使被包裹的子 Widget 保持活跃。 其次,如果用户向后滚动,则不会再次重新绘制子 Widget,这一点是通过 RepaintBoundaries 来保证,在默认情况下,每个子 Widget 都会被这个 Widget 包裹,它会让被包裹的子 Widget 仅仅绘制一次,以此获得更高的性能。 但,这样的问题在于,如果加载大量的图片,则会消耗大量的内存,最终可能使 App 崩溃。

图片方案是自研的外接纹理方案

虽然实现了一个App内一个内存缓存,并且将纹理和Flutter图片都存进去了,节省了内存空间,提高了内存使用率,但还是侵入了ImageCache源码,后续flutter engine的升级和代码维护,需要有额外的工作。

为什么flutter图片加载要采用外接纹理方案?

其实Flutter本身已具备加载图片的能力,Image组件就满足网络图片、本地图片、文件图片的加载。那为什么我们还需要实现其他图片加载方案呢?其实是因为Flutter图片组件功能上存在一些缺陷

  • 图片缓存没有持久化能力,无网环境下不支持显示图片。
  • 文件图片与原生环境不共用,导致图片资源文件重复。

我们不仅实现图片方案的本地能力复用,而且还能实现视频能力的纹理外接

内存性能不足

从整个APP的视角来说,采用原生图片方案的情况下,其实我们维护了两个大的缓存池:一个是Native的图片缓存,一个是Flutter侧的图片缓存。两个缓存无法互通,这无疑是一个巨大的浪费。特别是对内存的峰值内存性能产生了非常大的压力

调研梳理所有优化方向,确定图片压缩、更换图片组件、降低页面刷新次数三个方向

3.4 多页面内存优化

这个优化策略是真真被逼出来的。在对线上数据分析以后,我们发现Flutter页面栈有一个非常有意思的特点

多页面栈情况下,底层的页面不会被释放。即便是在内存非常紧张的情况下,也不会执行回收。这样就会导致一个问题:随着页面的增多,内存消耗会线性增长。这里占比最高的就是图片资源的占比了。

我们发现Flutter的layer层可以稳定感知到页面栈的变化。

4. 写代码时候, 内存总结:

Flutter内存优化是一个非常复杂的问题,其中涉及多个方面的优化策略。下面将从以下几个方面对Flutter的内存优化进行具体实现的总结。

一、减少Widget的创建和销毁

Widget的创建和销毁是Flutter中内存占用最大和最频繁的操作之一,在开发过程中,应该尽量减少Widget的创建和销毁。

1.1 重用现有的Widget

在同一个页面内,如果多个Widget具有相同的样式和行为,则可以共用同一个widget,而不是每个Widget都创建自己的。

1.2 使用Key避免重复构建

Flutter中相同类型的Widget,如果没有设置Key属性,则每次更新时都会创建新的Widget,即使两个Widget的props完全一致。相反,如果为Widget设置Key属性,则当Widget更新时,会尝试在它们之间保持一个一致的关系,从而减少重新构建它们所需要的树的大小。

1.3 避免状态管理器泄露

在Flutter中,每个控件对应一个状态,如果状态管理不当,可能会导致内存泄漏。因此,在组件销毁时必须释放所占用的内存。在实现中可以使用StatefulWidget和StatelessWidget等控件,这些控件会自动处理状态管理的问题。

二、优化图片资源

在Flutter中,图片资源可能会占用大量的内存,如果没有得到合适的管理和优化,很容易导致内存溢出的问题。

2.1 去掉不必要的图片

去掉那些不必要的图片,或者压缩那些需要的图片,以减少内存占用。

2.2 避免图片重复加载

重复加载的图片会占用更多的内存,因此应该尽量避免这种情况的发生。在Flutter中可以使用MemoryImage对图片进行缓存,提高图片的复用率。

三、避免资源泄漏

资源泄漏是内存占用过多的常见问题之一,在Flutter中也是如此。应该在开发过程中注意避免资源泄漏。

3.1 及时释放内存

当不再使用某个已分配的内存时,应该及时释放它,避免内存泄漏等问题。

3.2 避免“闭包”泄漏

在Flutter中,如果一个Widget包含了另一个Widget,则可能会发生“闭包”,即某个Widget的生命周期不正确地延长。为了避免这种情况的发生,可以使用StatelessWidget等控件,不包含状态变量,避免“闭包”泄漏。

四、合理管理内存

在Flutter中,应用程序使用的内存受到操作系统、硬件性能等多个因素的影响。为了合理地管理内存,需要进行特定的优化,以确保应用程序不会消耗过多的内存。

4.1 使用Navigator管理页面历史

在Flutter中,能够使用Navigator管理页面历史,并对页面进行统一的管理。使用Navigator可以对页面进行释放和回收,避免页面占用过多内存。

4.2 使用异步方式加载数据

采用异步方式优化数据加载,可以避免在程序运行时出现阻塞,使得应用程序可响应。异步方式可以优化数据请求、数据转换等操作,减少应用程序的响应时间。

综上所述,Flutter内存优化的具体实现有许多技巧和方法。开发人员可以根据实际情况综合运用各种优化策略,以提高应用程序的内存使用效率,提高用户的使用体验。

内存性能优化总结:

1. 内存泄露

2. listview优化

3. 图片优化, 图片外接纹理优化

4. 页面不可见,内存释放优化!

为了能够方便大家快速学习Flutter, 这里整理了Flutter学习路线图以及《Flutter Dart 语言编程入门到精通》&《Flutter实战:第二版》帮助大家配置相关环境,学习Flutter 的基本语法以及最后的项目实际利用。

第三章 异步之 Stream 详解

  • 什么是Stream
  • 单订阅流
  • 广播流
  • Stream Transformer
  • 总结

第四章 Dart标准输入输出流

  • 文件操作

第五章 Dart 网络编程

  • TCP服务端
  • TCP客户端
  • UDP服务端
  • UDP客户端
  • HTTP服务器与请求
  • WebSocket

第六章 Flutter 爬虫与服务端

  • Dart爬虫开发
  • Dart服务端
  • 创建Flutter项目演示
  • 总结

第七章 Dart 的服务端开发

  • 注册登录实现

第八章 Dart 调用C语言混合编程

  • 环境准备
  • 测试Dart ffi接口
  • 总结

第九章 LuaDardo中Dart与Lua的相互调用

  • Lua C API
  • 创建运行时
  • Dart调Lua
  • Lua调Dart

第三章:基础组件

  • 3.1 文本及样式
  • 3.2 按钮
  • 3.3 图片及ICON
  • 3.4 单选开关和复选框
  • 3.5 输入框及表单
  • 3.6 进度指示器

第四章:布局类组件

  • 4.1 布局类组件简介
  • 4.2 布局原理与约束(constraints
  • 4.3 线性布局(Row和Column
  • 4.4 弹性布局(Flex

第五章:容器类组件

  • 5.1 填充(Padding
  • 5.2 装饰容器(DecoratedBox
  • 5.3 变换(Transform
  • 5.4 容器组件(Container
  • 5.5 剪裁(Clip
  • 5.6 空间适配(FittedBox
  • 5.7 页面骨架(Scaffold

第六章:可滚动组件

  • 6.1 可滚动组件简介
  • 6.2 SingleChildScrollView
  • 6.3 ListView
  • 6.4 滚动监听及控制

第七章:功能型组件

  • 7.1 导航返回拦截(WillPopScope
  • 7.2 数据共享(InheritedWidget
  • 7.3 跨组件状态共享
  • 7.4 颜色和主题
  • 7.5 按需rebuild(ValueListenableBuilder
  • 7.6 异步UI更新(FutureBuilder、StreamBuilder
  • 7.7 对话框详解

第八章:事件处理与通知

  • 8.1 原始指针事件处理
  • 8.2 手势识别
  • 8.3 Flutter事件机制
  • 8.4 手势原理与手势冲突
  • 8.5 事件总线
  • 8.6 通知 Notification

第九章:动画

  • 9.1 Flutter动画简介
  • 9.2 动画基本结构及状态监听
  • 9.3 自定义路由切换动画
  • 9.4 Hero动画
  • 9.5 交织动画
  • 9.6 动画切换组件(AnimatedSwitcher
  • 9.7 动画过渡组件

第十章:自定义组件

  • 10.1 自定义组件方法简介
  • 10.2 组合现有组件
  • 10.3 组合实例:TurnBox
  • 10.4 CustomPaint 与 Canvas
  • 10.5 自绘实例:圆形背景渐变进度条
  • 10.6 自绘组件:CustomCheckbox
  • 10.7 自绘组件: DoneWidget
  • 10.8 水印实例: 文本绘制与离屏渲染

第十一章:文件操作与网络请求

  • 11.1 文件操作
  • 11.2 通过HttpClient发起HTTP请求
  • 11.3 Http请求库-dio
  • 11.4 实例:Http分块下载

第十二章:Flutter扩展

  • 12.1 包和插件
  • 12.2 Flutter Web

第十三章:国际化

  • 13.1 让App支持多语言
  • 13.2 实现Localizations
  • 13.3 使用Intl包
  • 13.4 国际化常见问题

第十四章:Flutter核心原理

  • 14.1 Flutter UI 框架(Framework
  • 14.2 Element、BuildContext和RenderObject
  • 14.3 Flutter启动流程和渲染管线
  • 14.4 布局(Layout)过程
  • 14.5 绘制(一)绘制原理及Layer

第十五章:一个完整的Flutter应用

  • 15.1 Github客户端示例
  • 15.2 Flutter APP代码结构
  • 15.3 Model类定义
  • 15.4 全局变量及共享状态
  • 15.5 网络请求封装
  • 15.6 APP入口及主页
  • 15.7 登录页
  • 15.8 多语言和多主题
最新文章
超逼真美女写真生成神器!探索AI绘画的宝藏工具攻略
DeepArt:这是一款将你的照片变成艺术风格的AI工具。用户可以上传自己的照片,然后选择不同的艺术风格,DeepArt会根据用户的选择生成俏丽的美女画像。它的优势在于简便易用,适合烧脑不太重的用户。不过,由于是将用户照片转化成艺术画作,
必看教程“微信小程序蜀山四川麻将外卦神器下载安装”(原来真的有挂)-知乎优质
亲,微信小程序蜀山四川麻将外卦神器下载安装这款游戏可以开挂的,确实是有挂的,很多玩家在这款游戏中打牌都会发现很多用户的牌特别好,总是好牌,而且好像能看到-人的牌一样。所以很多小伙伴就怀疑这款游戏是不是有挂,实际上这款游戏确
良心推荐:总结 Android 开发中必备的代码 Review 清单
本文收集了我自己工作以来提交代码前的所有检查点。事实证明,这样能有效提高自己的代码质量和功能的稳定性。所以推荐大家以后每次提交代码前,都可以看下这份 Review 清单哈。此外,可能还有些检查点我并没有发现,欢迎大家踊跃在评论区补
必看教程“微乐河北麻将万能开挂器通用版vivo”(确实是有挂)-知乎
微信打麻将是一款非常流行的棋牌游戏,深受广大玩家的喜爱。在这个游戏中,你需要运用自己的智慧和技巧来赢取胜利,同时还能与其他玩家互动。 在游戏中,有一些玩家为了获得更高的胜率和更多的金币而使用了开挂神器。开挂神器是指那些可以
钉钉打卡改位置神器2024免费版
钉钉打卡改位置神器2024免费版是一款非常专业的钉钉打卡定位应用,无需root,轻松安装使用。软件小巧,集成百度、高德、腾讯等主流地图,实现一键多系统定位验证。无论打卡签到还是位置考勤,都能迅速准确修改位置,让您的生活工作更加便捷
观点丨王晋斌:“去美元化”的三个维度
王晋斌中国人民大学国家发展与战略研究院研究员经济学院教授当前,“去美元化”已经成为全球热议的话题。理解“去美元化”可能需要多维度的思考。国际货币最终是国际市场选择的结果,主要靠的是货币的国际市场声誉机制;地缘政治会影响国际
甘肃企业360°精准营销攻略,全方位推广优化策略解锁市场新机遇
甘肃360推广优化,采用全方位策略,助力企业精准触达目标市场。通过精准定位、数据分析、创意优化等手段,提升企业品牌知名度,实现业务增长。随着互联网的普及和电子商务的快速发展,越来越多的企业开始意识到网络营销的重要性,在众多网
《李宏毅2022机器学习》HW1 记录
通过观察数据知道影响是否为阳性的有38+15个特征,前38位为id及one-hot表示地点的feature。代码为:结果如下: 注意还需要在config中将select_all改为False 再次提交后:得出结果分数更好了一点: Pytorch中做L
省点外链和商加加外链?
商加加外链工具是省点外链更名后的新名字。这一工具风靡企业市场,通过它,企业可以轻松实现将外链转化为企业微信的跳转链接,以便更好地服务客户和提升营销效果。省点外链改名成商加加外链工具并没有改变其核心功能。企业只需要在商加加外
百度快照被劫持百度网址安全中心提醒您:该页面可能已被非法篡改!
近期受世界杯的影响,我们Sinesafe接过很多中小企业网站频繁的被黑客入侵篡改了快照内容的网站安全问题导致打开网站被提示博彩页面,在搜索引擎中会被提示百度网址安全中心提醒您:该页面可能已被非法篡改!主要客户网站问题基本都是反复性质
相关文章
推荐文章
发表评论
0评