一摊阀、背景
測試人員發(fā)現(xiàn)一個問題耻蛇,就是反復(fù)快速進入詳情頁就會導(dǎo)致詳情頁崩潰,或者是app圖標全部不可見驹溃。
二城丧、解決過程
首先初步判斷肯定是有性能問題延曙,要么是內(nèi)存太大豌鹤,要么是內(nèi)存泄漏了。
1.我們通過devTools查看彩頁內(nèi)存
a枝缔、點擊Menory布疙。b、多次反復(fù)進入退出頁面愿卸。c灵临、點擊GC主動揮手回收對象。d趴荸、篩選我們的頁面
發(fā)現(xiàn)存在多個頁面對象儒溉。肯定是內(nèi)存泄漏了
2.初步解決一些常見的內(nèi)存泄漏問題
a发钝、圖片占用內(nèi)存過大有可能導(dǎo)致內(nèi)存泄漏顿涣,對圖片做了一系列優(yōu)化,防止內(nèi)存過大酝豪;以及把圖片相關(guān)需要釋放的都在頁面關(guān)閉時釋放掉涛碑。
b、把頁面里面其他需要釋放的一些對象孵淘,全部釋放掉了蒲障,減少部分內(nèi)存泄漏。
經(jīng)過測試,之前幾次就會導(dǎo)致頁面內(nèi)存爆炸揉阎,現(xiàn)在需要三十多次才會導(dǎo)致內(nèi)存爆炸庄撮。
3.還有其他內(nèi)存泄漏源,繼續(xù)查找
初步懷疑是getx框架有問題毙籽,所以在嘗試getx哪里有問題重窟。發(fā)現(xiàn)getx中的_routesKey會直接會導(dǎo)致頁面不會被釋放掉,加了如下一行代碼惧财,可以釋放掉頁面巡扇。
修改后,有的是可以釋放掉頁面垮衷,有的仍然無法釋放厅翔。
所以getx確實會導(dǎo)致頁面無法釋放,導(dǎo)致內(nèi)存泄漏搀突,但是代碼里面也還存在內(nèi)存泄漏刀闷。
4.繼續(xù)查找頁面里面內(nèi)存泄漏
由于頁面中組件有幾十個,設(shè)計的面積比較廣仰迁,所以很難直接定位到甸昏。于是進一和我繼續(xù)通過devtools查看內(nèi)存以及對比內(nèi)存差異减江,期望找到突破點曼验。但是內(nèi)存里面的對象有上百個征堪,很難定位到底是哪一個辆脸,起初還誤以為是attr和loader骂远,因為他們泄漏很多對象喧锦。但是實際很難說是他們泄漏導(dǎo)致頁面泄漏辆童,還是頁面泄漏導(dǎo)致這些對象泄漏抒痒。
最后我開始分析頁面返回的數(shù)據(jù)恰起,決定一個卡片去試修械,通過devtools分析可以判斷出是哪一個卡片泄漏,找到卡片泄漏后检盼,再一個組件去試肯污,找到Image組件導(dǎo)致內(nèi)存泄漏,最后再通過局部注釋代碼吨枉,最終定位到PreviewPptInheritWidget
class _PImageWidgetState extends State<PImageWidget> {
ui.Image? _rawImageContent;
ClipClipper? _oldClipper;
double? themeRadius;
PreviewPptInheritWidget? _previewPptInheritWidget;
...
@override
void initState() {
_previewPptInheritWidget = PreviewPptInheritWidget.ofUnDepend(context);
super.initState();
}
...
}
PreviewPptInheritWidget就是一個InheritedWidget子類蹦渣,所以他是PImageWidge的父類。在PImageWidge持有PreviewPptInheritWidget對象东羹,就會導(dǎo)致循環(huán)引用剂桥,最終導(dǎo)致內(nèi)存泄漏。
所以大家后續(xù)如果使用InheritedWidget系列組件属提,最好不要持有(成員變量)权逗,如果非要持有美尸,就需要在dispose的時候把InheritedWidget系列對象置空。
ios的同學(xué)可能非常熟悉循環(huán)引用斟薇,android同學(xué)之前幾乎遇不到循環(huán)引用师坎,android的同學(xué)可能要特別注意。
----------------------后續(xù)補充-------------------
后續(xù)又發(fā)現(xiàn)采用navigator.pop()退出頁面不會出現(xiàn)內(nèi)存泄漏堪滨。
如果是使用navigator.removeRoute()退出后臺的某個頁面胯陋,只會關(guān)閉頁面,但是頁面會出現(xiàn)內(nèi)存泄漏袱箱。
大概是pop會去回收頁面等完整流程遏乔,removeRoute只是移除路由。
有對這個十分了解的同學(xué)发笔,也可以評論區(qū)交流