Dart中的垃圾回收機制

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中最重要的收集方法,給當前不使用的值加上標記,然後等待回收其內存
    • 引用計數(不再使用) 跟蹤記錄每個值被引用的次數,當聲明瞭一個變量,並將一個引用類型賦值給該變量之後,引用次數加1,跟java一樣
    • 性能問題 垃圾收集器是週期運行的,而且如果變量分配的內存數量比較大,那麼回收工作量也是相當的大
  • Dart垃圾收集器

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

  • 調度器
    在Flutter引擎中,爲了最小化垃圾收集對應用程序和UI性能的印象,與垃圾收集器提供了hook,當引擎檢測到應用程序處於空閒狀態(沒有與用戶交互)會發出警報,爲垃圾收集器提供運行其收集階段而不影響性能的機會。並且垃圾收集器可以在這些空閒時間運行內存壓縮,從而較少內存碎片來優化內存
  • 新生代空間收集器
    此部分類似於Java的複製算法,用於清理壽命較短的對象,例如Stateless部件,雖然是會阻塞線程,但當與調度器結合使用,幾乎感知不到應用程序在運行期間的暫停,從本質上,新建的對象被分配給內存中的連續空間,在新建對象,會被分配到下一個可用空間,直到填充完分配的內存,但Dart使用的是一個凹凸的指針,所以這個過程非常快,分配新對象的空間由兩部分組成,任何時候只用一半,當一半滿後,活動的對象將複製到另一半空間中,一半就會全部清空,確定對象是否活動,收集器以根對象開始,進行檢測他們引用的內容,這一部分類似於Java的可達性算法,有引用的對象將會被複制到另一個空間中
  • 並行標記掃描收集器
    當對象達到一定的生命週期時,會被提上到另一個新的內存空間,由另一個收集器管理,此收集器有兩個階段:
    • 遍歷對象,標記仍在使用的對象
    • 掃描整個存儲器,並回收未標記的對象,然後清除所有標記

4.總結

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

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



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章