Flutter引擎 图片Graphics内存占用高的问题分析

2,226 阅读3分钟

概论简述

通过Android Profile工具观察可发现,Flutter列表页面(都是图片)滑动时Graphics内存一直增长,即使退出页面Graphics降低的也十分有限,如果是32位低端机型,会直接引发APP 崩溃

问题原因

DartVm的Image Widget在被销毁或者从DartVm内存缓存中移除的时候,只释放了DartVm的内存占用,但并没有释放引擎里图片的Graphics内存(引擎自己应该也有释放,理论上dart层已经没有引用了,引擎层可以自己释放,但从Android角度看,这里有个问题,Graphics内存Flutter和Native是共享的,假设flutter引擎自身的峰值是300MB,当我运行到290MB的时候,跳到native页面, 那290MB就一直占着,留给Naitve的就不多了。因此这个问题在混合项目里表现比较明显)

Flutter-图片内存分布.png

简单的数据验证

比较Android Native列表和Flutter 列表无限滚动的内存占用情况,为了数据比较的公正性,都用瀑布流(纯图片的形式)

Android

测试手机 红米9A Natvie页面,Graphics最后稳定在193M左右不再增长

Native-Graphics对比.png

Flutter,为了防止是Flutter图片库本身的缓存过大引起的问题,我们把Image_cache里的最大内存缓存从100MB降低到20MB,即使如此,Graphics最终会涨到350M左右,RedMi8A这种机型可能会直接闪退

这里要说明下为什么能比对,因为博主项目里的Flutter图片库和Native图片库是打通的,复用了Native图片库的磁盘缓存和图片请求通道。

Flutter-Graphisc-修复前.png

IOS

测试手机:xs max,内存峰值958MB,停止滚动后内存下降并不明显,还有812MB。

image.png

问题定位

通过查阅源码发现,ImageWidget在被销毁后没有调用引擎层Image对象的dispose方法。

FLutter-image_completer.png

问题修复

如上图所示,在Image_cache里 图片内容实际上是以ImageStreamCompleter的形式存在的,而同一个Completer有可能被多个ImageWidge同时引用,这就说明了ImageWidget的dispose不等于ImageStreamCompleter的dispose。因此对cache里的Completer,要做一个存活值的统计,待所有引用这个Completer的Widget都被dispose后,这个Completer才能被销毁。

定位到问题后,发现github上已经有大佬修复了 Framework提交 Engine提交,整体修改的内容还是比较多,有将近40个文件,大体的思路和我们上面说的是一样的,不过针对Gif的情况也做了优化,只保留当前帧。

这问题修复的版本是Flutter-2.0,所以还没升级的可以赶紧升了,不过从profile测试结果看,如果想让Flutter Graphics内存控制在220M左右,最好是把Flutter图片库的内存缓存调整到50M左右(针对低端机型),高端机型的Image_Cache可以调整的大点,Flutter默认的图片内存缓存是100MB。

当然这个也要看不同机型,进程是否64位(32位进程虚拟内存也是个大问题)等因素,下图是把ImageCache最大内存调整到50MB,RedMi8A的Graphics情况,一直稳定在220MB左右,相比修复前的300MB+,说明是有效的。

Android

测试手机 红米9A Graphics内存一直稳定在220MB左右,对比之前的数据,说明是有效的修复,Flutter framework侧图片最大内存阀值从100MB调整到50MB的情况下,Graphics内存下降30%,且不会随着列表滑动再增长 修复后.png

IOS

测试手机:xs max,内存峰值832MB,可以看到停止滚动后下降明显,下降到566MB。 image.png

ios内存峰值变化不明显猜测是Gc时机的不同,dispose仅仅是标记内存可以释放,但真正回收内存还是要等Gc,从最终结果看,ios的内存占用肯定是呈下降趋势的

参考文档:Android官方文档