Android 記憶體優化(二)DVM 和 ART 的 GC 日誌分析

劉望舒發表於2017-06-15

相關文章
Android記憶體優化系列
Java虛擬機器系列

前言

Java虛擬機器(三)垃圾標記演算法與Java物件的生命週期這篇文章中,提到了Java虛擬機器的GC日誌。DVM和ART的GC日誌與Java虛擬機器的日誌有較大的區別,這篇文章就對DVM和ART的GC日誌進行分析。

1.DVM的GC日誌

在 DVM 中,每次垃圾收集都會將GC日誌列印到 logcat 中,具體的格式為:

D/dalvikvm: <GC_Reason> <Amount_freed>, <Heap_stats>, <External_memory_stats>, <Pause_time>複製程式碼

可以看到DVM的日誌共有5個資訊,其中GC Reason有很多種,這裡將它單獨拿出來進行介紹。

引起GC原因

GC Reason就是指引起GC原因,有以下幾種:

  • GC_CONCURRENT:當堆開始填充時,併發GC可以釋放記憶體。
  • GC_FOR_MALLOC:當堆記憶體已滿時,app嘗試分配記憶體而引起的GC,系統必須停止app並回收記憶體。
  • GC_HPROF_DUMP_HEAP:當你請求建立 HPROF 檔案來分析堆記憶體時出現的GC。
  • GC_EXPLICIT:顯示的GC,例如呼叫System.gc()(應該避免呼叫顯示的GC,信任GC會在需要時執行)。
  • GC_EXTERNAL_ALLOC:僅適用於 API 級別小於等於10 ,用於外部分配記憶體的GC。

其他資訊

除了引起GC原因,其他的資訊為:

  • Amount_freed:本次GC釋放記憶體的大小。
  • Heap_stats:堆的空閒記憶體百分比 (已用記憶體)/(堆的總記憶體)。
  • External_memory_stats:API 級別 10 及更低階別的記憶體分配 (已分配的記憶體)/(引起GC的閥值)。
  • Pause time:暫停時間,更大的堆會有更長的暫停時間。併發暫停時間顯示了兩個暫停:一個出現在垃圾收集開始時,另一個出現在垃圾收集快要完成時。

例項分析

 D/dalvikvm: GC_CONCURRENT freed 2012K, 63% free 3213K/9291K, external 4501K/5161K, paused 2ms+2ms複製程式碼

這個GC日誌的含義為:引起GC的原因是GC_CONCURRENT;本次GC釋放的記憶體為2012K;堆的空閒記憶體百分比為63%,已用記憶體為3213K,堆的總記憶體為9291K;暫停的總時長為4ms。

2.ART的GC日誌

ART的GC日誌與DVM不同,ART 不會為沒有明確請求的垃圾收集列印GC日誌。只有在認為GC速度慢時才會列印GC日誌,更確切來說,僅在GC暫停超過5ms 或GC持續時間超過 100ms 時才會列印GC日誌。如果app未處於可察覺的暫停程式狀態,那麼它的GC不會被認為是慢速的。ART的GC日誌始終會記錄顯式的垃圾收集。

ART的GC日誌具體的格式為:

I/art: <GC_Reason> <GC_Name> <Objects_freed>(<Size_freed>) AllocSpace Objects,
 <Large_objects_freed>(<Large_object_size_freed>) <Heap_stats> LOS objects, <Pause_time(s)>複製程式碼

引起GC原因

ART的引起GC原因(GC_Reason)要比DVM多一些,有以下幾種:

  • Concurrent: 併發GC,不會使App的執行緒暫停,該GC是在後臺執行緒執行的,並不會阻止記憶體分配。
  • Alloc:當堆記憶體已滿時,App嘗試分配記憶體而引起的GC,這個GC會發生在正在分配記憶體的執行緒。
  • Explicit:App顯示的請求垃圾收集,例如呼叫System.gc()。與DVM一樣,最佳做法是應該信任GC並避免顯示的請求GC,顯示的請求GC會阻止分配執行緒並不必要的浪費 CPU 週期。如果顯式的請求GC導致其他執行緒被搶佔,那麼有可能會導致 jank(App同一幀畫了多次)。
  • NativeAlloc:Native記憶體分配時,比如為Bitmaps或者RenderScript分配物件, 這會導致Native記憶體壓力,從而觸發GC。
  • CollectorTransition:由堆轉換引起的回收,這是執行時切換GC而引起的。收集器轉換包括將所有物件從空閒列表空間複製到碰撞指標空間(反之亦然)。當前,收集器轉換僅在以下情況下出現:在記憶體較小的裝置上,App將程式狀態從可察覺的暫停狀態變更為可察覺的非暫停狀態(反之亦然)。
  • HomogeneousSpaceCompact:齊性空間壓縮是指空閒列表到壓縮的空閒列表空間,通常發生在當App已經移動到可察覺的暫停程式狀態。這樣做的主要原因是減少了記憶體使用並對堆記憶體進行碎片整理。
  • DisableMovingGc:不是真正的觸發GC原因,發生併發堆壓縮時,由於使用了 GetPrimitiveArrayCritical,收集會被阻塞。一般情況下,強烈建議不要使用 GetPrimitiveArrayCritical,因為它在移動收集器方面具有限制。
  • HeapTrim:不是觸發GC原因,但是請注意,收集會一直被阻塞,直到堆記憶體整理完畢。

垃圾收集器名稱

GC_Name指的是垃圾收集器名稱,有以下幾種:

  • Concurrent mark sweep (CMS):CMS收集器是一種以獲取最短收集暫停時間為目標收集器,採用了標記-清除演算法(Mark-Sweep)實現。 它是完整的堆垃圾收集器,能釋放除了Image Space之外的所有的空間。
  • Concurrent partial mark sweep:部分完整的堆垃圾收集器,能釋放除了Image Space和Zygote Spaces之外的所有空間。關於Image Space和Zygote Spaces可以檢視Android記憶體優化(一)DVM和ART原理初探這篇文章。
  • Concurrent sticky mark sweep:分代收集器,它只能釋放自上次GC以來分配的物件。這個垃圾收集器比一個完整的或部分完整的垃圾收集器掃描的更頻繁,因為它更快並且有更短的暫停時間。
  • Marksweep + semispace:非併發的GC,複製GC用於堆轉換以及齊性空間壓縮(堆碎片整理)。

其他資訊

  • Objects freed:本次GC從非Large Object Space中回收的物件的數量。
  • Size_freed:本次GC從非Large Object Space中回收的位元組數。
  • Large objects freed: 本次GC從Large Object Space中回收的物件的數量。
  • Large object size freed:本次GC從Large Object Space中回收的位元組數。
  • Heap stats:堆的空閒記憶體百分比 (已用記憶體)/(堆的總記憶體)。
  • Pause times:暫停時間,暫停時間與在GC執行時修改的物件引用的數量成比例。目前,ART的CMS收集器僅有一次暫停,它出現GC的結尾附近。移動的垃圾收集器暫停時間會很長,會在大部分垃圾回收期間持續出現。

例項分析

I/art : Explicit concurrent mark sweep GC freed 104710(7MB) AllocSpace objects, 
21(416KB) LOS objects, 33% free, 25MB/38MB, paused 1.230ms total 67.216ms複製程式碼

這個GC日誌的含義為:引起GC原因是Explicit ;垃圾收集器為CMS收集器;釋放物件的數量為104710個,釋放位元組數為7MB;釋放大物件的數量為21個,釋放大物件位元組數為416KB;堆的空閒記憶體百分比為33%,已用記憶體為25MB,堆的總記憶體為38MB;GC暫停時長為1.230ms,GC總時長為67.216ms。

參考資料
Investigating Your RAM Usage


歡迎關注我的微信公眾號,第一時間獲得部落格更新提醒,以及更多成體系的Android相關原創技術乾貨。
掃一掃下方二維碼或者長按識別二維碼,即可關注。

Android 記憶體優化(二)DVM 和 ART 的 GC 日誌分析

相關文章