Flutter中的垃圾回收機制

rhyme_lph發表於2019-03-21

1.介紹

Flutter主要使用Dart開發語言,在除錯和釋出兩個版本中,Dart RunTime是始終存在,但兩種版本下的構建方式有很大的差異

2.除錯和釋出版本下的差異

  • 除錯版本下 Dart編譯到裝置,包含三部分:

    1.Dart RunTime

    2.jit(Android下的實時編譯器)/interpreter(IOS下的解析器)

    3.除錯和分析服務

  • 釋出版本下

    1.Dart RunTime

兩種模式下都存在Dart RunTime,它包含了垃圾收集器,是例項化物件並變得無法訪問時分配和釋放記憶體的必要元件。

3.垃圾收集器競技場

對於Flutter而言,會建立很多物件:例如Stateless Widget從建立到應用程式的狀態發生改變或者變得不再可見時被銷燬和重建,大多數物件的生命週期是短暫的,若應用程式的UI變得相對複雜,可執行至上千個小部件

對於上面而言,很多人之前認為Flutter為什麼不用Java寫,為什麼不用Object-C寫,為什麼不用JavaScript寫,對於這些語言真的能勝任這麼頻繁的建立銷燬嗎?

  • Java垃圾收集器

    • jvm中java的記憶體分為四個部分:

      1.Java棧:主要作用存放方法執行的時候所有的資料,由棧幀代表一個方法的執行,每個方法從呼叫到執行完成在虛擬機器為一個棧幀的入棧和出棧,棧幀的資訊包括區域性變數表,棧運算元,動態連結,方法出口

      2.本地方法棧:主要為native服務,例如C、C++方法

      3.方法區:儲存被虛擬機器載入的類資訊、常量、靜態變數、即使編譯器編譯後的資料等

      4.堆區:所有通過new建立的物件的記憶體都在堆中分配,堆記憶體分為新的和舊的,剛new出來的物件放在新生代儲存,當記憶體不足時,虛擬機器會通過一系列演算法把新生物件移動到舊生代中去

    • 注意:

      1.當方法棧深度大於JVM深度的時候,就會棧溢位,例如:死迴圈(stackOverflow)

      2.新生代和舊生代都滿了,就會導致記憶體溢位(OutOfMemory)

    • 垃圾收集器的演算法

      垃圾回收主要針對堆記憶體,演算法主要包括垃圾的確定與收集、垃圾的回收、垃圾的回收時機

      1.引用計數法(廢棄):若物件被引用就會+1,沒有被引用的時候就回收,但引用計數法無法解決物件之間相互呼叫的問題

      2.可達性演算法:通過gc root物件開始搜尋,不可達的物件會被回收,引用的型別主要有強引用、弱引用,當存在強引用時寧願丟擲oom也不回收、但是弱引用的話,有可能被回收。

      3.標記清除法:搜尋發現沒有引用的物件直接回收,但是導致碎片過多

      4.複製演算法:搜尋掃描沒有引用的物件,開闢新的記憶體空間,將存活的物件複製到新的記憶體,舊的記憶體直接刪除,由於交換空間,適合物件比較少的時候,並且記憶體空間縮短一半

      5.標記整理法:在標記清除法的基礎上,清除掉不存活的物件,把後面存活的物件挪動過來,解決碎片問題

    • 上面的垃圾收集器演算法在jvm中沒有明確的規範,由各個廠商去實現

  • Object-C垃圾收集器

OC在早期版本中缺少較為完善的記憶體管理機制,需要開發者手動進行釋放,在Xcode4.2之後引入了ARC(Automatic Reference Counting)機制。

  • ARC機制

    ARC叫做自動引用計數,ARC中常見的所有權關鍵字:

    • assign 對應關鍵字__unsafe_unretained,指向的物件被釋放的時候,仍然指向之前的地址,容易引起野指標
    • copy 對應關鍵字__strong,在賦值的時候,呼叫copy方法
    • retain 對應關鍵字__strong
    • strong 對應關鍵字__strong
    • unsafe_unretained 對應關鍵字unsafe_unretained
    • weak 對應關鍵字weak
  • ARC內部實現

    ARC背後的引用計數主要依賴於三個方法:

    • retain 增加引用計數
    • release 降低引用計數,當引用計數為0時釋放物件
    • autorelease 在當前的auto release pool結束後,降低引用計數
  • JavaScript垃圾收集器

javaScript 具有垃圾自動收集機制,垃圾收集器會按照固定的時間間隔,週期性地執行這一炒作,具體到瀏覽器的實現,也可以指定收集時間

  • 垃圾收集的方法

    • 標記清除法 javaScript中最重要的收集方法,給當前不使用的值加上標記,然後等待回收其記憶體

    • 引用計數(不再使用)

    • 效能問題 垃圾收集器是週期執行的,而且如果變數分配的記憶體數量比較大,那麼回收工作量也是相當的大

  • Dart垃圾收集器

Dart的垃圾收集器是分代的,由兩個部分組成:新生代空間收集器、並行標記掃描收集器,還有一個重要的東西,就是排程器

  • 排程器

    在Flutter引擎中,為了最小化垃圾收集對應用程式和UI效能的印象,與垃圾收集器提供了hook當引擎檢測到應用程式處於空閒狀態(沒有與使用者互動)會發出警報,為垃圾收集器提供執行其收集階段而不影響效能的機會。並且垃圾收集器可以在這些空閒時間執行記憶體壓縮,從而較少記憶體碎片來優化記憶體

  • 新生代空間收集器

    此部分類似於Java的複製演算法,用於清理壽命較短的物件,例如Stateless部件,雖然是會阻塞執行緒,但當與排程器結合使用,幾乎感知不到應用程式在執行期間的暫停,從本質上,新建的物件被分配給記憶體中的連續空間,在新建物件,會被分配到下一個可用空間,直到填充完分配的記憶體,但Dart使用的是一個凹凸的指標,所以這個過程非常快,分配新物件的空間由兩部分組成,任何時候只用一半,當一半滿後,活動的物件將複製到另一半空間中,一半就會全部清空,確定物件是否活動,收集器以根物件開始,進行檢測他們引用的內容,這一部分類似於Java可達性演算法,有引用的物件將會被複制到另一個空間中

  • 並行標記掃描收集器

    當物件達到一定的生命週期時,會被提上到另一個新的記憶體空間,由另一個收集器管理,此收集器有兩個階段:

    • 遍歷物件,標記仍在使用的物件

    • 掃描整個儲存器,並回收未標記的物件,然後清除所有標記

4.總結

由上面所述,Dart的垃圾收集器方式參考了部分語言的實現,但需要注意的是,Dartisolates擁有自己的私有堆,彼此是獨立的,每個isolates執行在單獨的執行緒中,每個ioslates的垃圾收集事件不影響其它isolates的效能,所以isolates可以避免UI出現卡頓和很好的進行頻繁的回收操作,這就是dart作為Flutter的主要語言的原因之一。

參考: 1.Flutter: Don’t Fear the Garbage Collector

相關文章