Dart的內存回收機制介紹

Flutter使用dart語言做爲其開發語言和運行環境。dartruntime是一直存在的,可是在debugrelease模式下有一些區別。java

  • debug模式下,dart大部分組件都放在設備上,例如runtimeJIT(Android)interpreter(iOS)debugprofile services
  • release模式下,只剩下runtime,而這也是Flutter App可以運行起來的最基本組件。

debug和release模式下的dart組件

runtime中,存在一個在初始化對象時爲其分配內存,對象再也不被使用的時候回收內存的組件,即GC。 在Flutter中存在不少對象。以Stateless Widget爲例,其在State發生變化或者Widget不可見的時候不斷地發生重建和銷燬(注意,此處是指Widget樹中的Widget,對於Element樹和RenderObject樹來講,elementrenderObject是可變的,並且其初始化生成須要消耗不少資源。所以在大多數狀況下他們是會被回收利用的)。這些Widget的生命週期都很短,對於一個UI比較複雜的APP來講,可能會有數千個Widget須要被常常回收建立。算法


因此有些開發者可能會採起一些措施來避免太過頻繁的GC。好比爲了保持一個引用的Widget對象不會被回收,將其放在state中(這樣並非說真的不會被回收,只是建立回收的頻率被下降了,由於state是屬於element的,而element的生命週期是比較長的)。less

這麼作是沒有必要的,首先Widget是一個很輕量級的對象,它的建立和回收並不會佔用不少資源,真正佔用資源的是ElementRenderObject。其次dart 的GC機制可以快速有效的進行對象回收,不用擔憂Widget建立過多致使OOM出現。   oop

關於WidgetElementRenderObject的更多關係請參看這篇文章Flutter中的層級蛋糕。   post

Dart GC

和Jvm相似,dart中的GC是分代的,一個是年輕代,一個是老年代。若是熟悉Jvm內存機制的童鞋能夠快速略過這一部分,直接看最下面的結論。性能

1. schedule

首先介紹下dart中的調度機制:爲了最小化GC對APP和UI的性能影響(由於dart的GC有一種相似於JVM中stop the world的機制,致使APP對事件無響應、UI沒法刷新),GC經過與Flutterengine創建聯繫,在Flutter APP處於空閒、無用戶交互、或者在後臺的狀況下,engine通知GC進行回收工做。這樣就不會對APP和UI產生影響了。 同時GC還會使用滑動壓縮(相似於JVM中標記整理中的整理)的方法來減小內存碎片的數量,從而減小內存開銷。線程

2. 年輕代

dart內存中的年輕代和JVM中的年輕代很類似。 年輕代上存放的對象是那些生命週期較短,須要常常建立回收的對象,例如stateless widget。年輕代的GC比老年代的頻繁不少,速度也比老年代快。配合上schedule機制,咱們在APP運行的時候幾乎感受不到GC形成的卡頓。 在本質上,對象佔據了內存中的連續空間。隨着對象的建立,它們被分配給下一塊可用的內存空間,直到全部的內存都被佔滿了,而後進行GC。dart使用指針碰撞的方式來給這些對象分配空間(之因此沒有空閒列表的方法是由於dart在GC以後都會採用滑動壓縮的方式來把內存碎片清除掉)。 和JVM相似,dart的年輕代也分紅兩個部分,在任什麼時候候,只會有一部分被使用。 debug

年輕代中GC
如圖,在進行GC的時候,首先遍歷 from區域中的對象,判斷其是否能夠被回收(採用可達性分析方法),遍歷完成以後將不會被回收的對象複製到 to區域中,而後 from區域中的對象所有被回收掉。最後原來的 to區域就變成 from區域。 吐個槽,可能這種回收方式還會修改,改爲JVM中8:1:1的方式,由於每次都只能使用一半的內存,實在是太浪費內存了。 再補個圖供參考
年輕代GC

3. 老年代

當對象經歷過必定次數的GC仍然存在,或者其生命週期較長(我的猜想相似於elementRenderObject這種須要屢次複用,可變且建立比較耗費性能),將其放入老年代區域中。 老年代採用標記整理的方法來回收對象。設計

  • 在標記的時候,該線程中內存區域是處於不可修改的狀態,相似於JVM中stop the world,因此這個時候可能會致使ANR(只是相似於ANR的表現,其產生緣由仍是不同的),可是因爲dart優秀的schedule機制和老年代GC頻率很低的緣由,基本上不會出現這個問題。
    老年代GC

須要注意的是,若是APP不支持弱年代假設(即大多數對象的生命期都很短;從年老對象到年輕對象的引用很是少),上面的分代設計就不那麼有效了,可是考慮到Flutter中的WidgetElementRenderObject關係,咱們不須要擔憂這個問題。指針

4. isolate

與JVM內存模型不一樣的是,dart中每一個isolate都有本身的內存空間,其各自的GC不會影響到其餘isolate的。因此咱們能夠經過把部分佔用內存空間較大且生命週期較短的對象方法其餘isolate中,這樣即便另一個isolate GC了,並不會對咱們顯示UI的isolate形成影響。

dart vm與dalvik vm區別
與isolate相關的知識請參看小德大佬的文章:

總結

dart java
判斷對象可被回收算法 可達性分析 可達性分析
年輕代GC算法 複製 1:1 複製 8:1:1
老年代GC算法 標記整理 標記整理
是否發生stop the world

引用文章

相關文章
相關標籤/搜索