


摘 要
Android系統中虛擬機通過提供垃圾回收機制(Garbage Collection,GC)實現自動內存管理,但是這一機制在簡化應用開發人員內存管理負擔、避免內存泄漏問題的同時也給系統帶來了內存駐留、響應延遲、并發阻礙等負面影響。Android 5.0以后,Google用ART虛擬機替換掉Dalvik虛擬機,為了更好地利用ART虛擬機自動內存管理的特性,應用開發人員有必要深入理解ART虛擬機垃圾回收機制的實現。本文從GC算法和堆分配過程研究了ART虛擬機GC系統,本文提出的動態設置堆利用率以減少Full GC的方式優化GC系統性能,最后測試多組應用程序對比優化前后在GC次數、暫停時間和GC總時間等方面的差異。整體平均暫停時間相較于優化前降低20%以上,而平均GC總次數減少5次以上,證明本文實驗條件下動態設置堆利用率的方式優化Full GC對GC系統性能的提高是有效的。
【關鍵詞】ART虛擬機 GC系統 堆利用率
ART虛擬機GC子系統對Android系統和上層應用程序的穩定性和流暢性具有非常大的影響。本文提出的動態設置堆利用率以減少Full GC的方式優化GC系統性能,最后測試多組應用程序對比優化前后在GC次數、暫停時間和GC總時間等方面的差異。
1 垃圾回收機制
1.1 堆內存的對象分配和釋放
ART虛擬機實現的垃圾回收機制是一種自動內存管理機制,對象分配和收集都在堆上進行。傳統的C/C++是沒有GC的,初始化對象然后分配內存空間需要手動地malloc/new,當需要銷毀對象時候需要手動地free/delete。內存管理使用這種模式導致至少兩個明顯的問題:指針懸掛和內存泄漏。而采用了垃圾回收機制的JAVA中,軟件開發人員只需考慮申請內存,垃圾回收機制可以根據一定的規則判斷出內存空間中的“垃圾”,自動釋放“垃圾”占用的內存。不過付出的代價是GC耗費額外的系統資源,并會暫停用戶進程,嚴重時候明顯影響用戶體驗,例如系統卡頓、動畫掉幀等。與此同時潛在一個危機是,一旦出現內存泄漏或溢出的問題,如果開發人員不了解虛擬機內存管理機制,那么解決這類問題就變得棘手。
1.2 ART虛擬機的GC算法
Java垃圾回收機制采用的主流GC算法包括:引用計數算法(Reference counting)、標記-清除算法(Mark-Sweep GC)、復制算法(Copying GC)、標記-合并算法(Mark-Compact GC),后面三種算法屬于追蹤式算法,如表1所示。
ART虛擬機默認的兩種垃圾回收技術,一種是Mark-Sweep,另一種是Semi-Space GC。應用在前臺時候設置GC采用Mark-Sweep算法,應用運行在后臺時候設置GC采用Semi-Space算法。本文主要關注運行在前臺的Mark-Sweep算法的執行性能。
Mark-Sweep:它的大致思想是,將所有的對象在內存中的位置記錄在位圖A中。然后,從所有對象的根出發,掃描根對象的所有引用,掃描根對象的所有引用的引用,一層層逐級掃描,直到葉子節點的對象。在這個逐級掃描的過程中,將涉及到的對象的位置都記錄在位圖B中。掃描結束后,對比兩張位圖A和B:所有A中置位的位置,卻沒有B中被置位,被視為垃圾。并根據位圖中的索引檢索得到對象,然后釋放該對象占用的內存。
Semi-Space GC: 它的特點是需要有兩個space空間,其中一個備用。另一個被系統拿來分配對象。在做垃圾掃描的時候,將所有在空間A中被有效引用的對象移動到空間B中。那么在空間A中剩余的對象就是垃圾了,直接釋放垃圾對象所在的整個空間就可以了。這個回收速度很快。
1.3 ART虛擬機的堆結構
堆(Heap)可以是由字構成的連續數組,也可以組織成不連續、由字構成的塊的集合,與數據結構中堆的概念不同,內存管理中的堆用來存放各種用戶或系統申請的數據對象,在內存中占據非常大的空間,因此也常將堆空間用內存來代替。
如圖1所示,ART虛擬機中Mark-Sweep GC堆結構分6個區域,分別為Image Space、Zygote Space、Non-Moving Space、Large Object Space、Main Space以及Backup Space。Image Space存放預加載的系統類對象,Zygote Space在Zygote進程和應用程序進程之間共享,存放Zygote進程所需的預加載的類、資源和對象,它是Zygote進程在fork第一個應用程序時候從Non-Moving Space劃分出來的。Non-Moving Space是用來存放不需要移動的對象的,有些對象例如類對象、類方法和類成員變量對象一經加載后,就基本一直存在,頻繁的移動此類對象代價較大且沒必要。Large Object Space是用來分配大對象的,當分配的原子類型數組的大小大于3個內存頁時,就把此對象分配在Large Object Space中。其他的對象就分配在Main Space中。
2 ART虛擬機GC執行過程分析
2.1 GC執行過程
ART運行時與Dalvik虛擬機一樣,也使用了Mark-Sweep算法進行垃圾回收。但是在ART運行時里的Mark-Sweep算法更加優化。原來Dalvik虛擬機里的垃圾回收需要暫停兩次,標記根集階段和重新標記根集階段,兩次暫停時間如果很長或很頻繁就會對用戶進程有很大的負面影響。而在ART虛擬機取消了第一次暫停,原來在第一次暫停所需要做的工作分給了線程自己,在整體上提高了ART運行時的垃圾回收的性能。
如圖2所示,GC執行過程分為以下幾個階段:
2.1.1 標記階段
首先標記根集對象,包括當前線程棧中的對象、當前進程所有已經加載的類及類中的靜態引用等。然后從根集對象開始遞歸標記出所有可達對象。
2.1.2 暫停階段
標記階段在標記根集的時候沒有暫停其他線程,所以在標記的過程中對象的引用關系可能會發生變化,如果不處理這種變化就會導致對象的錯標或漏標,后面清掃垃圾時就有可能把存活對象也回收了,導致程序的非正常運行。所以需要有個暫停階段,來處理標記階段中對象的引用關系發生變化的對象。
2.1.3 清除階段
經過了前面的兩個階段,標記清除算法認為的存活對象都在標記位圖(Mark Bitmap)中標記出來了。接下來回收沒有被標記的內存塊。
2.1.4 終結階段
在回收垃圾后有個收尾工作,會對經過垃圾回收后的堆根據需求進行裁剪,也會對堆的預留空閑內存進行重新設定。
2.2 堆內存分配過程
對于標記清除算法,ART有三種GC策略:分代垃圾回收(Sticky GC)、局部垃圾回收(Partial GC)以及全局垃圾回收(Full GC)。分代垃圾回收只回收上一次GC到本次GC之間申請的內存。局部垃圾回收不回收Image Space和Zygote Space空間的內存。全局垃圾回收回收除了Image Space之外的空間的垃圾。GC 暫停時間:Sticky GC < Partial GC < Full GC,回收垃圾的效率則反之。為了在垃圾回收性能和垃圾回收效率之間追求平衡,ART采用了一種漸進式的分配策略。而影響GC暫停時間和總時間最大的Full GC是本文優化目標。
GC時候,首先會進行一次輕量級的GC, GC完成后嘗試分配。如果分配失敗,則選取下一個GC策略,再進行一次輕量級GC。每次GC完成后都嘗試分配,直到三種GC策略都被輪詢了一遍還是不能完成分配,則進入下一階段。第二階段允許堆進行增長的情況下進行對象的分配,如果還是分配失敗,則會進行一次允許回收軟引用的GC。如果還是分配失敗就進入第三階段如果對象是不可移動對象,則ART會把它分配在Non-Moving Space。如果對象是可移動的對象,那么就進行一次同構空間壓縮,壓縮后也增加了分配成功的可能性。
3 性能調優
3.1 與GC相關堆屬性值
如圖3所示,在build.prop中一般會設置如下幾個與Android ART虛擬機垃圾回收相關的屬性值:前三個值是控制Java堆的總大小的,包括堆的起始大小、堆的增長上限等,與手機的硬件配置相關。堆利率、堆最小空閑內存和堆最大空閑內存三個變量對垃圾回收的某些性能有影響。GC觸發后,垃圾回收器回收了應用不再使用的垃圾對象,這樣應用的空閑內存就可能很大或者由于回收垃圾不夠多導致空閑內存還是很小。空閑內存很大,Android系統內存利用率就低,當然不會把這么大一塊內存都給應用程序的,出于這種考慮系統會根據應用預先設定的堆利用率(HeapTargetUtilization)、最大和最小空閑內存數(heapmaxfree、heapminfree)等參數來調整此空閑內存的大小;如果此空閑內存很小,那么勢必此空閑內存將很快分配光,下次GC會來的很快,所以遇到這種情況,ART會擴大此空閑內存的大小堆利用率(HeapTargetUtilization)、最大空閑內存(heapmaxfree)和最小空閑內存(heapminfree)在代碼里的變量名為:utilization,max_free_,min_free_。
如圖4所示,堆利用率(utilization)算可以出理論上所需的空閑內存,堆利用率按照谷歌的推薦一般設為0.75。考慮另外兩個限制值:最小空閑內存(min_free_)和最大空閑內存(max_free_)。也就是需要把預留空閑內存控制在兩倍的最小空閑內存和兩倍的最大空閑內存之間。這樣獲得的target_size才是堆的最終大小。
3.2 使用堆目標利用率減少Full GC頻率
如2.2節描述,當對象分配請求得到滿足時候,用戶進程就會繼續執行,不過如果對象分配失敗,則說明當前的堆大小是不能滿足進程需求的。事實上對于現有應用運行用戶體驗來看,ART虛擬機仍有很多可以優化空間,比如一些大型游戲或消耗資源的應用中,Full GC頻率仍然較高,使得暫停時間和GC頻率的增加。為了獲得更有效率的內存管理,需要在應用運行過程中,人為干涉GC處理,本文通過設計自適應的對增長機制來滿足應用運行狀態的動態改變。使用dalvik.system.VMRuntime類提供的接口setTargetHeapUtilization方法增強應用程序堆內存的處理效率。比如在應用程序onCreate的時候就調用下面的一個用于設置利用率大小的函數VMRuntime.getRuntime().setTargetHeapUtilization即可,本文實現堆利用率動態調整實現流程如圖5所示。
當完成申請的時候系統會繼續執行,根據堆利用率設置新的理想堆大小,減少完全GC的方法就是在當系統剩余的內存不足以完成本次申請操作時,將本次申請的對象轉移到緩沖堆,此時根據堆目標利用率設置堆的理想大小,這樣由于內存不足產生的完全GC就可以避免,這樣就避免的長時間的暫停,而且堆此時已經生成為理想大小,不會使得后面堆空間不足而循環產生GC動作增加GC頻率。
4 實驗設計與性能測試
本實驗采用的硬件平臺是Google公司的Nexus4,軟件調試平臺是Android Studio。對0xbenchmark 和用戶應用優化前后GC次數、GC 的暫停時間進行統計分析。
4.1 基于0xbenchmark性能測試
0xBenchmark中有專門測垃圾回收性能的模塊。測試的算法是遞歸自頂向下和遞歸自底向上創建完全二叉樹,以及創建大的浮點數組,對于內存塊的創建還分為長生命周期對象和臨時對象,長生命周期對象的引用要在測試函數運行完畢時才會丟失,而臨時對象在創建完畢后即被丟失,測試的標準是創建所需時間,暫停時間以及GC總時間等。
4.2 基于實際應用運行log信息性能測試
動態設置堆利用率以減少FULL GC優化手段帶來最直接的影響就是減少垃圾回收暫停時間,雖然0xbenchmark有Android的VM垃圾回收性能測試,但是不能符合正常情況下應用對象大小并不固定的事實。所以除了使用0xbenchmark評估性能表現外,本文選取了幾個常見的應用作為測試對象。分析這些應用運行過程的log信息,主要是暫停時間,來評估優化效果。
4.3 性能測試結果
比較表2和表3的情況,原生垃圾回收信息和優化后的垃圾回收信息,FullGC的暫停時間是占用平均暫停時間很大比例,優化FullGC對于GC平均暫停時間有很大貢獻,另一方面優化后GC總次數降低了,總次數的減少是因為本文增加一個設置堆利用率的過程,滿足新對象對堆空間的需求,以Full GC需求減少進而減少了總GC次數。從應用的整體來看,平均暫停時間相較于優化前降低20%以上,而平均GC總次數減少5次以上。本文實驗條件下動態設置堆利用率的方式優化Full GC對GC系統性能的提高是有效果的。
5 結束語
垃圾回收機制實現自動內存管理的代價是額外消耗系統資源、阻塞用戶進程的執行等。本文從GC算法和堆分配過程研究了ART虛擬機GC系統運作機制,提出的動態設置堆利用率以減少Full GC的方式優化GC系統性能。設計實驗測試了多組應用程序,結果顯示整體平均暫停時間相較于優化前降低20%以上,平均GC總次數減少5次以上。
參考文獻
[1]Andrei Frumusanu. A Closer Look at Android RunTime (ART) in Android L [EB/OL].http://www.anandtech.com/show/8231/a-closer-look-at-android-runtime-art-in-android-l/
[2]付卓.Dalvik內存管理機制分析與優化[D].南京:東南大學集成電路學院(碩士學位論文),2013.
[3]黃洋.Android內存管理中的垃圾收集性能優化[D].南京:東南大學電子學院(碩士學位論文),2014.
[4]夏迪迪.Dalvik虛擬機垃圾回收算法的分析與性能優化[D].無錫:東南大學集成電路學院(碩士學位論文),2015.
[5]王浩.面向用戶體驗的GC策略分析與優化[D].無錫:東南大學集成電路學院(碩士學位論文),2015.
[6]老羅.ART運行時Mark-Sweep GC簡要介紹和學習計劃 [EB/OL].http://blog.csdn.net/luoshengyang/article/details/44513977,2014.
[7]Android開發工具文檔[EB/OL].http://developer.android.com/tools/index.html,2011.
[8]0xbench [EB/OL].https://code.google.com/p/0xbench/,2011.
作者簡介
張加帥(1990-),男,碩士研究生學歷。研究方向為程序語言自動內存管理。
作者單位
東南大學微電子學院 江蘇省無錫市 214135