当系统发生内存回收,会有logcat信息。
那么如何正确理解内存回收信息呢?

有两种类型的回收信息log:虚拟机时代的log和ART时代的log

虚拟机时代的log


每次产生GC时间,log格式如下:

D/dalvikvm(PID): GC_Reason Amount_freed, Heap_stats, External_memory_stats, Pause_time

示例:

D/dalvikvm( 9050): GC_CONCURRENT freed 2049K, 65% free 3571K/9991K, external 4703K/5261K, paused 2ms+2ms

格式解析
GC_Reason

  • GC_CONCURRENT
    当堆开始填满时触发并发GC,回收内存
  • GC_FOR_MALLOC
    当应用尝试分配内存时,此时堆已经用完,系统不得不停止app和回收内存。
  • GC_HPROF_DUMP_HEAP
    当请求创建一个HPROF文件(用于分析内存),触发GC。
  • GC_EXPLICIT
    代码中明确的调用gc()(不建议如此使用)
  • GC_EXTERNAL_ALLOC
    这种内存回收在API版本为10及更小的版本发生。

Amount freed
本次回收的内存大小

Heap stats
堆内存状态:空闲堆内存百分比使用中的对象数/堆内存总大小

External memory stats
在API 10及之下版本,外部分配占用的堆内存大小/外部分配最大限制内存大小

Pause time
大的堆栈会有更长停顿时间。log显示两个停顿时间:开始回收时的停顿时间和结束时的停顿时间。

内存回收的log收集起来,查找堆状态的增长信息(3571K/9991K),如果一直在增长,可能存在内存泄漏。

ART时代log


不像虚拟机,ART不会记录非明确调用执行的GC。GC被打印仅仅是因为认为运行慢了。更准准确的说,GC停顿超过5ms或者GC过程停顿超过100ms。如果app没有处于可察觉停顿状态(如后台运行),那么没有GC被认为是慢的。确定的GC调用总会被记录。

ART的内存回收包含以下信息

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 : 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 Reason
什么触发了GC和回收了什么?原因如下

  • Concurrent
    并发GC不会阻塞app线程。该GC运行在后台线程且不会阻止内存申请。
  • Alloc
    该GC被发起,是因为在堆内存已经申请使用完后再次申请内存。这种情况,GC运行在内存申请的线程。
  • Explicit
    内存回收被app直接的请求执行,例如,调用gc()。类似虚拟机,ART中最好的使用时详细系统GC,尽量避免直接调用GC。直接调用GC不鼓励使用,因为直接调用会阻塞申请内存的线程和不必要的浪费CPU时间片。直接调用GC,其他线程被抢占,也会导致无法预料的异常(如卡顿、抖动、停止运行)。
  • NativeAlloc
    本地内存申请存在压力,如Bitmaps或者渲染申请内存,会触发内存回收。
  • CollectorTransition
    回收被堆管理策略转换导致的。在运行时改变GC策略,触发GC(例如在app可察觉停顿状态下,app改变GC策略)。回收策略转换包括将所有对象从free-list backed space拷贝到 bump pointer space (or visa versa)。
  • HomogeneousSpaceCompact
    同构空间压缩是指从空闲列表空间到空闲列表空间的压缩,通常发生在应用程序移动到一个难以察觉的暂停进程状态时。这样做的主要原因是减少RAM的使用和整理堆的碎片。
  • DisableMovingGc
    这不是一个真正的GC事件原因,但需要注意的是,回收会因为调用GetPrimitiveArrayCritical被阻塞。当并发堆压缩发生时。一般情况下,强烈不鼓励使用GetPrimitiveArrayCritical,因为它对移动收集器有限制。
  • HeapTrim
    这不是一个真正的GC事件原因,但需要注意的是,回收会被阻塞指导堆内存被整理完成。

GC Name
ART有多种不同的GC可被执行。

  • Concurrent mark sweep (CMS)
    收集回收整个堆内存除了图像部分内存。
  • Concurrent partial mark sweep
    几乎全堆收集,收集除了图像和zygote以外的所有空间。
  • Concurrent sticky mark sweep
    分代收集器,只能释放自上次GC以来分配的对象。这种垃圾收集比完全或部分标记清理运行得更频繁,因为它速度更快,暂停时间更短。
  • Marksweep + semispace
    非并发的、复制的GC,用于堆转换和同质空间压缩(以整理堆的碎片)。

Objects freed
本次回收,从非大对象空间回收的对象数。
Sized freed
本次回收,从非大对象空间回收的字节数
Large Object freed
本次回收,从大对象空间回收的对象数
Heap stats
剩余堆内存百分比,使用中的对象数/整个堆大小。
Pause times
一般来说,停顿时间与GC运行时修改的对象引用的数量成比例。目前,ART CMS的GCs只有一次停顿,在GC快结束的时候。运行的GC有一个很长的停顿,在大部分GC持续时间中都是如此。

如果在log中看到大量的GC事件,关注堆状态增长的变化(如上例中的25MB/38MB)。如果堆使用一直在增长,不曾变小过,那么可能存在内存泄漏。另外,如果看到的GC都是因为“Alloc”触发的,那么app运行在一个接近所有堆容量的状态,在不久就会出现OOM的情况。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐