内存水位升高导致的稳定性问题严重影响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内存空间状态调换。

当对象达到一定的生命周期,会被移到老年代内存空间管理,这种垃圾收集策略有两个阶段

首先遍历对象图,并标记仍在使用的对象。

扫描整个内存,并回收任何未标记的对象,然后清除所有标志。这种内存清理的频率较低,并且在扫描回收阶段需要暂停Dart runtime,回收成本较高,比较适合Flutter中大量StatelessWidget的模式(大部分都存放在新生代)。

另外,当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模式下进行。

1.打开Observatory URL的Web页面。

运行app,在控制台中查找类似输出日志listening on ws://127.0.0.1:64673/hXsWR_ZOsGk=/ws, 表示当前连接的VM地址,输入到浏览器就可以看到Observatory主界面,显示了dart vm一些基础信息,具体使用方法可以参考 官方文档,这里不再详细描述,我们重点关注右下角的allocation profile选项。

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

3.点击对应class查看具体应用实例,点击对应实例进入查看引用路径,就能找到没有导致释放的引用变量,结合业务代码具体分析,就能发现泄漏的源头。

这里有一点需要注意,Observatory显示的Dart VM占用的内存信息要远远小于Android Profile/Xcode检测出的内存大小,因为存在部分只有系统工具能检测出的内存区块,例如一些完全不依赖于DartVM的skia对象,并且layer在engine中创建时并不能明确知道大小,所以采用虚拟近似值代替。

//engine/lib/ui/painting/engine_layer.cc

...

size_t EngineLayer::GetAllocationSize() {

// Provide an approximation of the total memory impact of this object to the

// Dart GC. The ContainerLayer may hold references to a tree of other layers,

// which in turn may contain Skia objects.

return 3000;

};

下面我们总结了几种常见内存泄漏的场景,在Java中都可以一一对应找到类似的场景,大家在业务开发中注意避免

常见内存问题

示例代码:

class DownloadManager extends Object {

......

abstract class DownloadListener {

void completed(DXTemplateItem item);

void failed(DXTemplateItem item, String errorInfo);

}

static List listenerList = List();

static void downloadSingleTemplate(DXTemplateItem template, DownloadListener listener) async {

listenerList.add(listener);

...

}

...

修改方法:手动取消注册或回调

// 移除

static void removeDownloadListener(DownloadListener listener) {

if (listener != null && listenerList != null && listenerList.contains(listener)) {

listenerList.remove(listener);

}

}

示例代码:

void _resolveImage() {

final ImageStream newStream =

widget.image.resolve(createLocalImageConfiguration(context));

assert(newStream != null);

_updateSourceStream(newStream);

}

修改方法:在图片组件被销毁时正确释放资源

@override

void dispose() {

...

_imageInfo.image.dispose();

_imageInfo = null;

super.dispose();

}

示例代码:

/// 通过asset读取Json

Future> loadJsonAsset(String assetPath) async {

_rootBundle ??= PlatformAssetBundle();

final String jsonStr = await _rootBundle.loadString(assetPath);

return json.decode(jsonStr);

}

修改方法:

/// 通过asset读取Json

Future> loadJsonAsset(String assetPath) async {

_rootBundle ??= PlatformAssetBundle();

final String jsonStr = await _rootBundle.loadString(assetPath, cache: false);

return json.decode(jsonStr);

}

PlatformAssetBundle继承于CachingAssetBundle,会在app整个生命周期中缓存读取的数据,如果不是需要频繁访问的话建议cache参数设置为false

以上内容介绍了作者在实践中遇到的Flutter内存问题解决思路,给出了内存泄漏定位方法。优化后能在一定程度上减小内存压力,避免不必要的内存占用。闲鱼在内存优化的方向上还有很多需要继续探索的地方,正在做的包括对图片库的缓存改造,layer层内存检测工具等等,朝着不断优化flutter性能体验努力。

flutter 获取定位_定位Flutter内存问题如何做相关推荐

  1. flutter 图片路径_【Flutter开发工具推荐】Flutter资源管理利器:Flr

    前言 作为一名Flutter开发者,如果你正在烦恼如何简单快捷地在pubspec.yaml中为多张图片.文本.字体资源添加声明,如果你正在烦恼如何简单安全地在代码中引用资源,那么现在你可以很简单地解决 ...

  2. flutter刷新页面_用Flutter实现58App的首页

    背景 Flutter作为全新跨平台应用框架,在页面渲染和MD开发上优势明显,可谓是业界一枝独秀.正好最近有这样的一个机会学习Flutter开发,我们便尝试用它开发一个MD风格的较复杂页面,来比较跟原生 ...

  3. flutter ios打包_关于Flutter iOS打包报错的问题

    今天升级Xcode之后,使用打包机打iOS包的时候,报了一个错,报错的信息如下: note: Using new build systemnote: Building targets in paral ...

  4. flutter 应用场景_【Flutter 1-12】Flutter手把手教程Dart语言——什么是泛型和泛型的使用场景...

    泛型 如果你查看数组的API文档,你会发现数组List的实际类型为List.<> 符号表示数组是一个泛型(或参数化类型)通常使用一个字母来代表类型参数,比如E.T.S.K 和 V 等等. ...

  5. flutter获取Android照片地址,Flutter项目安卓配置启动图片

    1.打开android\app\src\main\res\drawable\launch_background.xml, android:gravity="center" andr ...

  6. flutter 禁止冒泡_【Flutter】Switch开关组件

    说明 Switch 是一个切换按钮组件,通常用于设置的选项里.Switch 的原点颜色,横条颜色都可以设置,此外原点可以以图片形式显示. 可以使用Switch.adaptive构建组件: Switch ...

  7. flutter 获取定位_从头开发一个Flutter插件(二)高德地图定位插件

    在上一篇文章从头开发一个Flutter插件(一)开发流程里具体介绍了flutter插件的具体开发流程,从创建项目到发布.接下来将会为Flutter天气项目开发一个基于高德定位sdk的flutter定位 ...

  8. ios 高德获取定位_概述-iOS 定位SDK | 高德地图API

    简介 高德 iOS 定位 SDK 提供了不依赖于地图定位的定位功能,开发者可以无地图显示的场景中便捷地为应用程序添加定位功能. iOS定位SDK提供了单次定位.连续定位.逆地理信息.地理围栏等功能. ...

  9. java获取url锚点_定位与锚点

    文档流: 普通流:上至下,左至右 浮动流:元素添加了float:: 定位流:添加了定位属性 定位 position:; static 默认值 absolute 绝对定位(脱离文档流,不占位:默认参考浏 ...

最新文章

  1. 【VS实践】代码调试与错误处理
  2. TIOBE 8 月榜单:Groovy 和 Objective-C 重返前二十
  3. 【云原生AI】Fluid + JindoFS 助力微博海量小文件模型训练速度提升 18 倍
  4. sizeof和strlen()区别
  5. Python Day11
  6. netbeans html 格式化,在NetBeans IDE8.0中怎么把html的文件转换成jsp格式
  7. 这个结构体对齐输出有意思
  8. Python自动化运维之常用模块—logging
  9. Elasticsearch--入门-_get查询数据 乐观锁字段---全文检索引擎ElasticSearch工作笔记006
  10. LeetCode(1009)——十进制整数的反码(JavaScript)
  11. 不宜使用Selenium自动化的10个测试场景
  12. jieba 同义词_jieba分词详解
  13. 03 三维地图添加切片图层
  14. ubuntu 下使用unixbench 这种benchmark 工具测试系统性能
  15. 计算机DNS怎么配置,如何设置计算机dns?电脑dns设置教程
  16. adb调试android设备
  17. 王者怎么看微信以前玩过的服务器,王者荣耀如何查看微信帐号在那个区玩过
  18. Python里的pygame游戏安装环境
  19. layui时间选择器选择周和季度
  20. uniapp手动实现国际化【中英文】

热门文章

  1. 如何选择合适的大数据分析平台的类型
  2. 《Python cookbook》笔记一
  3. 数据分析数据挖掘(三)
  4. 打怪升级的monteCarlo仿真方法
  5. 数据交互什么意思_学习编程怎么样才可以不枯燥?什么是前端语言?
  6. 泊松图像融合算法代码实现_部分多曝光图像融合算法(含少数通用图像融合算法)代码下载链接...
  7. java的set和get方法实例化_java反射机制 调用get set 方法 | 学步园
  8. html超链接的设置,HTML超链接怎么设置
  9. Spark将数据写入Mysql
  10. [linux]linux IO 5种方式