日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

定位Flutter内存问题很难么?

發布時間:2024/9/3 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 定位Flutter内存问题很难么? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
簡介:flutter內存泄漏定位

作者:閑魚技術-三蒞

內存水位升高導致的穩定性問題嚴重影響app用戶體驗,所以開發者們非常關注Flutter的內存表現。隨著Flutter業務越來越多,閑魚也面臨著oom導致的crash率提升的問題,下面我們結合項目中實際遇到的內存問題和解決思路跟大家分享下flutter內存優化的經驗。

本文分為三個部分

  • 了解Dart VM內存分配及銷毀原理
  • 通過Observatory工具分析內存泄漏,減少不必要的內存占用
  • Flutter中常見內存泄漏場景有哪些,如何在業務應用中避免踩坑
  • Dart VM內存分配及銷毀原理

    DartVM的垃圾回收機制分兩個階段,新生代(New Generation)和老年代(Old Generation)。

    新生代用來存儲生命周期較短的對象,由兩個內存空間組成,Active內存空間用來分配新對象,inActive內存空間用來作為備用空間,DartVM的內存分配策略非常簡單,創建對象時只需要在現有堆上移動指針,內存增長始終是線形的,省去了查找可用內存段的過程。每個Isolate有自己獨立的Heap,相互之間無法共享內存,這樣可以實現無鎖的快速分配。

    一旦Active的內存空間被填滿,垃圾回收器會從根對象開始遍歷檢查檢查所有對象的引用狀態,沒有被引用的對象標記為dead狀態,非dead狀態的對象在下次內存回收事件中會被復制到inActive內存空間,清除Active內存空間,最后Active和inActive內存空間狀態調換。

    當對象達到一定的生命周期,會被移到老年代內存空間管理,這種垃圾收集策略有兩個階段,1. 首先遍歷對象圖,并標記仍在使用的對象。2. 掃描整個內存,并回收任何未標記的對象,然后清除所有標志。這種內存清理的頻率較低,并且在掃描回收階段需要暫停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模式下進行。

  • 打開Observatory URL的Web頁面。
  • 運行app,在控制臺中查找類似輸出日志listening on ws://127.0.0.1:64673/hXsWR_ZOsGk=/ws, 表示當前連接的VM地址,輸入到瀏覽器就可以看到Observatory主界面,顯示了dart vm一些基礎信息,具體使用方法可以參考 官方文檔,這里不再詳細描述,我們重點關注右下角的allocation profile選項。

  • 點擊右下角allocation profile選項后,操作app進入想要分析的Flutter頁操作,退出該頁面,點擊頁面右上角 GC按鈕觸發手動GC,查看Class,發現有部分DX Class內存占用,這類class本應該只有在目標分析頁會出現,退出目標分析也后手動GC會被完全釋放,但是這里任然能看到相關內存占用,說明產生了內存泄漏。
  • 點擊對應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<DownloadListener> 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);}}
  • 資源未關閉或釋放導致內存泄露,例如ImageStream的圖片資源有沒有被正常關閉導致的內存泄漏。
  • 問題代碼:

    void _resolveImage() {final ImageStream newStream =widget.image.resolve(createLocalImageConfiguration(context));assert(newStream != null);_updateSourceStream(newStream);}

    修改方法:在圖片組件被銷毀時正確釋放資源

    @overridevoid dispose() {..._imageInfo.image.dispose();_imageInfo = null;super.dispose();}
  • PlatformAssetBundle().loadString通過asset讀取String內容會一直緩存讀取的內容,造成內存無法釋放
  • 問題代碼:

    /// 通過asset讀取Json Future<Map<String, dynamic>> loadJsonAsset(String assetPath) async {_rootBundle ??= PlatformAssetBundle();final String jsonStr = await _rootBundle.loadString(assetPath);return json.decode(jsonStr); }

    修改方法:

    /// 通過asset讀取Json Future<Map<String, dynamic>> 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帶圖片的長列表滑動會造成內存一直上漲,flutter在1.17版本對這個問題做了優化,具體改動可以參考pr 14265
  • 以上內容介紹了閑魚在實踐中遇到的Flutter內存問題解決思路,給出了內存泄漏定位方法。優化后能在一定程度上減小內存壓力,避免不必要的內存占用。閑魚在內存優化的方向上還有很多需要繼續探索的地方,正在做的包括對圖片庫的緩存改造,layer層內存檢測工具等等,朝著不斷優化flutter性能體驗努力。

    原文鏈接:https://developer.aliyun.com/article/770316?

    版權聲明:本文內容由阿里云實名注冊用戶自發貢獻,版權歸原作者所有,阿里云開發者社區不擁有其著作權,亦不承擔相應法律責任。具體規則請查看《阿里云開發者社區用戶服務協議》和《阿里云開發者社區知識產權保護指引》。如果您發現本社區中有涉嫌抄襲的內容,填寫侵權投訴表單進行舉報,一經查實,本社區將立刻刪除涉嫌侵權內容。

    總結

    以上是生活随笔為你收集整理的定位Flutter内存问题很难么?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。