記憶體對象管理大緻分兩部分,一個是iOS的引用計數銷毀機制,一個是垃圾回收機制,最近在學習flutter,Dart語言的記憶體管理和Java他們一樣是垃圾回收,記錄一下,以便之後檢視。
在學習Flutter的過程中,我們知道Widget隻是最終渲染對象(RenderObject)的配置檔案,它會在build的時候頻繁的銷毀和建立,那麼,我們不需要擔心他的建立和銷毀帶來的性能問題嗎?
記憶體對象管理大緻分兩部分,一個是iOS的引用計數銷毀機制,一個是垃圾回收機制,最近在學習flutter,Dart語言的記憶體管理和Java他們一樣是垃圾回收,記錄一下,以便之後檢視。
在學習Flutter的過程中,我們知道Widget隻是最終渲染對象(RenderObject)的配置檔案,它會在build的時候頻繁的銷毀和建立,那麼,我們不需要擔心他的建立和銷毀帶來的性能問題嗎?
其實大可不必,因為Dart針對Flutter的Widget的建立和銷毀專門做過優化,這也是Flutter在多種語言中選擇Dart的一個重要因素,甚至我們還可以刻意利用這一點。
Flutter使用Dart作為開發語言和運作時機制,Dart一直保留着運作時機制,無論是在調試模式(debug)還是釋出模式(release),但是兩種建構方式之間存在很大的差異。
- 在調試模型下,Dart将所有的管道(需要用到的所有配件)全部裝載到裝置上:運作時,JIT(the just-in-time)編譯器/解釋器(JIT for Android and interpreter for iOS),調試和性能分析服務
- 在釋出模式下,會除去JIT編譯器/解釋器依然保留運作時,因為運作時是Flutter App的主要貢獻者

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是分代的(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),進而通過減少記憶體碎片來最大程度地減少記憶體開銷。
階段一:Young Space Scavenger
這個階段主要是清理一些壽命很短的對象,比如StatelessWidget。當它處于阻塞時,它的清理速度遠快于第二代的mark、sweep方式。并且結合排程,完成可以消除程式運作時的暫停現象。
本質上來講,對象在記憶體中被配置設定一段連續的、可用的記憶體空間,直接被配置設定完為止。Dart使用bump pointer(注解:如果像malloc一樣,維護free_list再配置設定,效率很低。)配置設定新的空間,處理過程非常快。
配置設定了新對象的新空間,被為兩部分,稱之為semi spaces。一部分處于活動狀态,另一部分處于非活動狀态。新對象配置設定在活動狀态,一旦填充完畢,依然存活的Object,就會從活動狀态copy到非活動狀态,并且清除死亡的Object。這個時候非活動狀态變成了活動狀态,上面的步驟一次重複。(注解:GC來完成上面的步驟)
為了确定哪些Object是存活的或死亡的,GC從根對象開始檢測它們的應用。然後将有引用的Object(存活的)移動到非活動狀态,直接所有的存活Object被移動。死亡的Object就被留下,然後清楚回收記憶體;
有關此的更多資訊,請檢視Cheney算法。
階段二: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)。