本文主要講述Dalvik與ART兩種Android虛擬機,在GC時產生log信息的含義,便於分析。
一、Dalvik虛擬機
1.1 GC log格式
Dalvik虛擬機,每一次GC打印內容:
格式: D/dalvikvm: <GC_Reason> <Amount_freed>,
<Heap_stats>, <External_memory_stats>, <Pause_time>
格式: D/dalvikvm: <GC觸發原因> <GC釋放空間>, <堆統計信息>,
<外部內存統計>, <暫停時間>
樣例: D/dalvikvm( 9050): GC_CONCURRENT freed 2049K,
65% free 3571K/9991K, external 4703K/5261K, paused 2ms+2ms
含義:GC_CONCURRENT,回收了2049K內存,堆上空閒內存比65%,堆內存活動內存大小:3571K/ 堆內存總大小:9991K。本次總共暫時4ms.
##1.2 含義解析
- GC Reason(GC觸發原因)
- GC_CONCURRENT:當已分配內存達到某一值時,觸發併發GC;
- GC_FOR_MALLOC:當嘗試在堆上分配內存不足時觸發的GC;系統必須停止應用程序並回收內存;
- GC_HPROF_DUMP_HEAP: 當需要創建HPROF文件來分析堆內存時觸發的GC;
- GC_EXPLICIT:當明確的調用GC時,例如調用System.gc()等;
- GC_EXTERNAL_ALLOC: 僅在API級別爲10或者更低時(新版本分配內存都在Dalvik堆上)
-
Amount freed GC回收的內存大小
-
Heap stats 堆上的空閒內存百分比 (活動對象的數量)/(堆的總大小)
-
External memory stats API級別爲10或者更低:(已分配的內存量)/ (即將發生垃圾的極限)
- Pause time(暫停時間) 較大的堆將有較大的暫停時間。併發暫停時間顯示有兩個停頓:一個是垃圾回收的開頭和另一接近垃圾回收的結尾。
1.3 小結
根據這些日誌信息不斷積累,尋找出增加的堆統計信息(在上面的例子中,3571K/9991K值)。如果這個值繼續增加,可能有內存泄漏。
二、ART虛擬機
ART的log不同於Dalvik的log機制,正常情況不會打印非明確調用的GCs的log信息。GCs打印出來的log信息都是被認爲是執行比較緩慢的信息,更準確地說,就是GC暫停的時間超過5ms或者GC執行的總時間超過100ms。 如果app不處於暫停的感知狀態,那麼沒有GC會被認爲是緩慢的。但是明確地調用GCs會記錄log信息。
2.1 GC log格式
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)>
格式: I/art: <GC觸發原因> <GC名稱> <釋放對象個數>(<釋放字節數>)
AllocSpace Objects, <釋放大對象個數>(<釋放大對象字節數>) <堆統計> LOS objects, <暫停時間>
樣例: 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
含義:
##2.2 含義解析
2.2.1 GC Reason(GC觸發原因)
-
Concurrent
併發GC,不會使app線程掛起,該GC是在後臺線程運行的,也不會阻止內存分配。 -
Alloc
當堆內存已滿時,app嘗試申請內存,會觸發該GC。在這種情況下,垃圾回收發生在正在分配內存的線程。 -
Explicit
明確的調用垃圾回收,比如gc().與dalvik一樣,應儘可能避免明確調用gc。不建議使用,由於程序的GC會阻塞分配線程和不必要的CPU週期,如果其他線程獲取搶佔資源也可能導致jank。 -
NativeAlloc
垃圾回收是由於native內存過重而觸發的,例如Bitmaps或者RenderScript分配的對象。 -
CollectorTransition
由於堆過渡觸發的。收集器轉換包括拷貝所有的對象從一個自由列表支持空間到指針空間,也包括反過來拷貝。目前垃圾回收器傳輸僅發生在低內存設備的app進程狀態轉移,包含從一個可感知的暫停狀態轉換到非暫停可感知狀態,或者非暫停態到暫停態。 -
HomogeneousSpaceCompact
齊性空間壓縮是指空閒列表到壓縮的空閒列表空間,通常發生在當app已經移動到暫停可感知的進程狀態。這樣做的主要原因是減少了內存使用情況和堆碎片整理。 -
DisableMovingGc
這並非真實的GC觸發原因,只是標記垃圾回收被阻塞,由於並非堆壓縮正在發生時使用了GetPrimitiveArrayCritical。由於其對移動收集器的限制,使用GetPrimitiveArrayCritical是強烈不建議。 -
HeapTrim
這並非GC觸發原因,只是標記垃圾回收被阻塞直到堆整理完成。
2.2.2 GC Name(GC名稱)
ART有多種不同的GC
-
Concurrent mark sweep (CMS)
完整的堆垃圾回收器,能釋放除去圖片空間之外的所有的垃圾。 -
Concurrent partial mark sweep
絕大多數的堆垃圾回收器,能釋放除去圖片和zygote空間之外的所有的垃圾。 -
Concurrent sticky mark sweep
一般性的垃圾回收器,只能釋放上一次GC所關聯的對象。該垃圾回收器運行得更常用,有更低的暫停時間。 -
Marksweep + semispace
非併發gc,複製GC用於堆過渡以及齊性空間壓縮(堆碎片整理)。
2.2.3 Objects_freed
當前GC,從非大對象空間回收到的對象數
2.2.4 Size_freed
當前GC,從非大對象空間回收到的字節數
2.2.5 Large_objects_freed
當前GC,從大對象空間回收到的對象數
2.2.6 Large_object_size_freed
當前GC,從大對象空間回收到的字節數
2.2.7 Heap_stats
堆上的空閒內存百分比 (活動對象的數量)/(堆的總大小)
2.2.8 Pause_time
一般性地,暫停時間跟GC正在運行時引用對象被改變的對象數成正比。目前,ART的CMD GC僅有一次停頓,出現在GC的結尾附近。移動GC有一個長的暫停時間持續在GC的大多數期間。
小結
- 當看到大量的GC log信息在logcat,可查看堆統計(如樣例中 5MB/38MB)。如果這個值持續增長,並且一直不見它變小,那可能發生了內存泄露。
- 如果看到GC觸發條件是
Alloc
,那當前環境已經接近堆內存的上限了,在不久後很快會出現OOM。
參考
http://developer.android.com/tools/debugging/debugging-memory.html