Flutter 之 Dart的垃圾回收器

在學習Flutter的過程中,我們知道Widget只是最終渲染對象(RenderObject)的配置文件,它會在build的時候頻繁的銷燬和創建,那麼,我們不需要擔心它的創建和銷燬帶來的性能問題嗎?

其實大可不必,因為Dart針對Flutter的Widget的創建和銷燬專門做過優化,這也是Flutter在多種語言中選擇Dart的一個重要因素,甚至我們還可以刻意利用這一點。

下面這篇文章解析了Dart的GC(Garbage Collector),對它做了個翻譯以及部分內容的解析,包括一些排版,有不對的地方大家多多指正。

原文地址:https://medium.com/flutter/flutter-dont-fear-the-garbage-collector-d69b3ff1ca30

Flutter使用Dart作為開發語言和運行時機制,Dart一直保留著運行時機制,無論是在調試模式(debug)還是發佈模式(release),但是兩種構建方式之間存在很大的差異。

  • 在調試模型下,Dart將所有的管道(需要用到的所有配件)全部裝載到設備上:運行時,JIT(the just-in-time)編譯器/解釋器(JIT for Android and interpreter for iOS),調試和性能分析服務。
  • 在發佈模式下,會除去JIT編譯器/解釋器依然保留運行時,因為運行時是Flutter App的主要貢獻者。
Flutter 之 Dart的垃圾回收器

debug和release

Dart的運行時包括一個非常重要的組件:垃圾回收器,它主要的作用就是分配和釋放內存,當一個對象被實例化(instantiated)或者變成不可達(unreachable)。

在Flutter運行過程中,會有很多的Object。

  • 在StatelessWidget在渲染前(其實上還有StatefulWidget),他們被創建出來。
  • 當狀態發生變化的時候,他們又會被銷燬。
  • 事實上,他們有很短的壽命(lifespan)。
  • 當我們構建一個複雜的UI界面時,會有成千上萬這樣的Widgets。

所以,作為Flutter開發者,我們需要擔心垃圾回收器不能很好的幫助我們管理這些嗎?(是不是會帶來很多的性能問題呢)

  • 當Flutter頻繁的創建和銷燬這些Widget(Objects),我們是否需要很迫切的限制這種行為呢?
  • 非常普遍,對於新的Flutter開發者來說,當一個Widget的狀態不需要改變時,他們會創建引用的Widget,來替代State中的Widget,以便於不會被銷燬或者重建。

不需要這樣做

擔心Dart的GC是沒有任何事實根據的(沒有必要),這是因為它分代(generational)架構和實現,可以讓我們頻繁創建和銷燬對象有一個最優解。在大多數情況下,我們只需要Flutter引擎按照它的方式創建和銷燬這些Widgets即可。

Dart的GC

Dart的GC是分代的(generational)和由兩個階段構成:the young space scavenger(scavenger針對年輕一袋進行回收) and parallel mark sweep collectors(sweep collectors針對老一代進行回收)

註解:事實上V8引擎也是這樣的機制

調度安排(Scheduling)

為了讓RG最小化對App和UI性能的影響,GC對Flutter引擎提供了hooks,hooks被通知,當Flutter引擎被偵測到這個App處於閒置的狀態,並且沒有用戶交互的時候。這就給了GC一個空窗期來運行它的手機階段,並且不會影響性能。

垃圾收集器還可以在那些空閒間隔內進行滑動壓縮(sliding compaction),從而通過減少內存碎片來最大程度地減少內存開銷。


Flutter 之 Dart的垃圾回收器

階段一:Young Space Scavenger

這個階段主要是清理一些壽命很短的對象,比如StatelessWidget。當它處於阻塞時,它的清理速度遠快於第二代的mark、sweep方式。並且結合調度,完成可以消除程序運行時的暫停現象。

本質上來講,對象在內存中被分配一段連續的、可用的內存空間,直接被分配完為止。Dart使用bump pointer(註解:如果像malloc一樣,維護free_list再分配,效率很低。)分配新的空間,處理過程非常快。

分配了新對象的新空間,被為兩部分,稱之為semi spaces。一部分處於活動狀態,另一部分處於非活動狀態。新對象分配在活動狀態,一旦填充完畢,依然存活的Object,就會從活動狀態copy到非活動狀態,並且清除死亡的Object。這個時候非活動狀態變成了活動狀態,上面的步驟一次重複。(註解:GC來完成上面的步驟)

為了確定哪些Object是存活的或死亡的,GC從根對象開始檢測它們的應用。然後將有引用的Object(存活的)移動到非活動狀態,直接所有的存活Object被移動。死亡的Object就被留下;

有關此的更多信息,請查看Cheney算法。

Flutter 之 Dart的垃圾回收器

階段二:Parallel Marking and Concurrent Sweeping

當對象達到一定的壽命(在第一階段沒有被GC回收),它們將被提升由第二代收集器管理的新內存空間:mark-sweep。

這個階段的GC有兩個階段:第一階段,首先遍歷對象圖(the object graph),然後標記人在使用的對象。第二階段,將掃描整個內存,並且回收所有未標記的對象。

這種GC機制在標記階段會阻塞,不能有內存變化和UI線程也會被阻塞。但是由於短暫的對象在Young Space Scavenger階段以及被處理,所有這個階段非常少出現。不過由於Flutter可以調用收集時間,影響的性能也會被降到最低。

但是如果引用程序不遵守分代的機制,反而這種情況會經常發生。但是由於Flutter的Widget的機制,所有這種情況不經常發生,但是我們還是需要了解這種機制。

Isolate

值得注意的是,Dart中的Isolate機制具有私有堆的概念,彼此是獨立的。每個Isolate有自己單獨的線程來運行,每個Isolate的GC不影響其他線程的性能。使用Isolate是避免阻塞UI和減輕密集型任務的好方法(註解:耗時操作可以使用Isolate)。

總結

到這裡你應該明白:Dart使用了強大的分代GC,以最大限度的減少Flutter中GC帶來的性能影響。

所以,你不需要擔心Dart的垃圾回收器,這個反而是我們應用程序的核心所在。


分享到:


相關文章: