Dalvik-GC-Specification

Dalvik GC Specification

GC

Dalvik VM定义了四种类型的GC:

  • GC_FOR_MALLOC: 尝试在堆上分配内存创建实例对象,但是堆上的Freed内存不够时触发。
  • GC_CONCURRENT: 已分配内存达到一堆程度时,VM自动触发执行,开启并行GC来回收内存。
  • GC_EXPLICIT: System.gc、VMRuntime.gc、Process SIGUSR1信号触发。
  • GC_BEFORE_OOM: VM为了避免OOM而尝试做的最后一次GC。

在安卓源码的 /dalvik/vm/alloc/Heap.cpp 文件里可以查到对四种类型GC的行为特性定义:

  • GC_FOR_MALLOC: 仅回收Active堆;非并行GC;不回收软引用。
  • GC_CONCURRENT: 仅回收Active堆;并行GC;不回收软引用。
  • GC_EXPLICIT: 回收Active堆及Zygote堆;并行GC;不回收软引用。
  • GC_BEFORE_OOM: 回收Active堆及Zygote堆;非并行GC;回收软引用。

非并行GC会挂起VM内所有线程

一些定义在 /dalvik/vm/alloc/HeapSource.cpp 中的重要常量:

  • HEAP_TRIM_IDLE_TIME_MS: 间隔多长时间执行一次 GC_CONCURRENT 来释放不被使用的内存。
  • CONCURRENT_START: 当Freed内存小于该值时执行 GC_CONCURRENT
  • CONCURRENT_MIN_FREE: 128KB + CONCURRENT_START

Java Heap Allocation

Basics

在Dalvik中, Java Heap由2种Heap组成。

  • Zygote Heap: 管理Zygote进程在启动过程中预加载和创建的各种对象。
  • Active Heap: 在Zygote进程fork第一个子进程之前创建的;之后无论是Zygote进程还是其子进程,都在Active堆上进行对象分配和释放。
HeapBitmap:
  • LiveHeapBitmap: 记录上次GC之后,还存活的对象。
  • MarkHeapBitmap: 记录当前GC中还存活的对象。上次GC后存活的但是当前GC不存活的对象,就是需要释放的对象。
  • MarkStack: Davlk使用标记-清除算法进行GC。在标记时,使用MarkStack递归检查被引用的对象,即在当前GC中存活的对象。

Behavior

1. 正常分配内存

在Dalvik分配内存成功,则将新创建的对象增加到Dalvik内部的一个引用表中(保存在引用表中的对象在执行GC时会添加到GC Roots中);如果分配内存失败,则直接调用函数抛出OOM异常。
在成功分配内存之后,会执行 /dalvik/vm/alloc/HeapSource.cpp 中的dvmHeapSourceAlloc函数,该函数会做三件很重要的事情:

  1. countAllocation函数中记录已经分配的字节数和对象数。
  2. dvmHeapBitmapSetObjectBit函数中将新分配对象的LiveHeapBitmap标记为置为1。
  3. 检查Active堆内存,如果大于了阈值(CONCURRENT_START),则通知GC线程进行GC_CONCURRENT
2. 内存不足,增长堆然后分配内存

在调用 dalvik/vm/alloc/HeapSource.cpp 中的函数dvmHeapSourceAllocAndGrow来分配内存时,如果不能直接调用dvmHeapSourceAlloc分配成功,则尝试将SoftLimit去掉;如果还不能正常分配内存,则调用heapAllocAndGrow来设置Java堆的大小的为最大值,之后再尝试分配内存。
如果这次分配内存成功了,则调用snapIdealFootprint来重新设置Java堆的大小(从刚刚设置的最大值返回到一个适合的值),否则使用setIdealFootprint来恢复Java堆的大小。