GC

Table of Contents

1. GC

1.1. Overview

1.2. Collector

1.2.1. Hierachy

digraph "art::gc::collector::GarbageCollector"
{
  edge [fontname="Helvetica",fontsize="10",labelfontname="Helvetica",labelfontsize="10"];
  node [fontname="Helvetica",fontsize="10",shape=record];
  Node0 [label="art::gc::collector\l::GarbageCollector",height=0.2,width=0.4,color="black", fillcolor="grey75", style="filled", fontcolor="black"];
  Node0 -> Node4 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node4 [label="art::gc::collector\l::ConcurrentCopying",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node0 -> Node5 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node5 [label="art::gc::collector\l::MarkCompact",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node0 -> Node6 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node6 [label="art::gc::collector\l::MarkSweep",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node6 -> Node7 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node7 [label="art::gc::collector\l::PartialMarkSweep",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node7 -> Node8 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node8 [label="art::gc::collector\l::StickyMarkSweep",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node0 -> Node9 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node9 [label="art::gc::collector\l::SemiSpace",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node9 -> Node10 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node10 [label="art::gc::collector\l::ZygoteCompactingCollector",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
}

art_collector.png

1.2.2. Collector Type

  1. kCollectorTypeMS, kCollectorTypeCMS MarkSweep
  2. kCollectorTypeSS, kCollectorTypeGSS SemiSpace
  3. kCollectorTypeMC MarkCompact
  4. kCollectorTypeCC ConcurrentCopying
  5. kCollectorTypeHomogeneousSpaceCompact

1.2.3. Gc Type

  1. kGcTypeSticky StickyMarkSweep 属于 sticky gc
  2. kGcTypePartial PartialMarkSweep, SemiSpace, MarkCompact, ConcurrentCopying 都属于 partial gc
  3. kGcTypeFull MarkSweep 属于 full gc

1.2.4. GcRoot

1.2.4.1. RootType
enum RootType {
    kRootUnknown = 0,
    kRootJNIGlobal,
    kRootJNILocal,
    kRootJavaFrame,
    kRootNativeStack,
    kRootStickyClass,
    kRootThreadBlock,
    kRootMonitorUsed,
    kRootThreadObject,
    kRootInternedString,
    kRootFinalizing,  // used for HPROF's conversion to HprofHeapTag
    kRootDebugger,
    kRootReferenceCleanup,  // used for HPROF's conversion to HprofHeapTag
    kRootVMInternal,
    kRootJNIMonitor,
};
1.2.4.2. non-concurrent gc root
  1. local IndirectReferenceTable
  2. StackHandleScope
  3. monitor_enter_object
  4. stack frame(shadow/quick)
1.2.4.3. concurrent gc root
  1. global IndirectReferenceTable
  2. intern_table_
  3. class_linker_
    1. class_roots_
    2. dex_caches_
1.2.4.4. MarkRoots
MarkRoots:
  Runtime::VisitRoots(visitor)
    VisitNonConcurrentRoots(visitor);
    VisitConcurrentRoots(visitor, flags);

VisitNonConcurrentRoots:
  thread_list_->VisitRoots(visitor);
    thread->VisitRoots(visitor);
      // monitor_enter_object
      visitor->VisitRootIfNonNull(&tlsPtr_.monitor_enter_object, RootInfo(kRootNativeStack, thread_id));
      // local indirect_reference_table
      tlsPtr_.jni_env->locals.VisitRoots(visitor, RootInfo(kRootJNILocal, thread_id));
      // jni monitor
      tlsPtr_.jni_env->monitors.VisitRoots(visitor, RootInfo(kRootJNIMonitor, thread_id));
      // StackHandleScope
      HandleScopeVisitRoots(visitor, thread_id);
      // frame: shadow and quick
      RootCallbackVisitor visitor_to_callback(visitor, thread_id);
      ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context, visitor_to_callback);
      mapper.WalkStack();
  VisitNonThreadRoots(visitor);

VisitConcurrentRoots:
  // intern_table_
  intern_table_->VisitRoots(visitor, flags);
  class_linker_->VisitRoots(visitor, flags);
    class_roots_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
  heap_->VisitAllocationRecords(visitor);

visitor:
  // 不同的 collector 有不同的 visitor, 以 MS 为例, visitor.VisitRoots 就是把对象放在 MarkStack 中

1.2.5. Collector.Run

GarbageCollector::Run():
  collector.RunPhases()
    // 各种 collector 的 RunPhases 有些差别, 以 CMS 为例
    MarkSweep::RunPhases():
      InitializePhase()
      if (IsConcurrent()):
        // for CMS, MarkingPhase is concurrent
        MarkingPhase();
        {
          ScopedPause pause (this);
          PausePhase ()
        }
        // ReclaimPhase is concurrent
        ReclaimPhase()
      else:
        {
          ScopedPause pause(this);
          // for MS, MarkingPhase is not concurrent
          MarkingPhase();
          PausePhase()
        }
        // ReclaimPhase is concurrent even for MS
        ReclaimPhase()
1.2.5.1. MarkingPhase
MarkingPhase:
  // 所谓 BindBitmaps, 实际指的是 bind mark bitmaps, 即在扫描之前就
  // 把一些满足条件的对象在 mark bitmaps 中置位, 例如:
  // 1. 若一个 space 属于 kGcRetentionPolicyNeverCollect, 则该 space
  // 中的所有 live bitmaps 中的对象都被放到 mark bitmaps 中
  // 2. 若当前的 collector 为 partial_mark_sweep, 则 zygote space 的
  // live bitmaps 会被复制到 mark bitmaps, 表示 zygote space 在
  // partial_mark_sweep 时不会被回收
  // 3. 若当前为 sticky_mark_sweep, 则 DlMallocSpace 中的 live bitmaps
  // 也会被复制到 mark bitmaps, 这个看起来有些奇怪: live bitmaps 中不
  // 一定都是被 mark 的对象啊...
  // 与 dalvik 不同的是, 新分配的对象不保存在 live bitmaps, 而是先保存
  // 在 allocation stack 中, 所以 live bitmaps 代表的实际上是上一次 GC
  // 结束时的 mark bitmaps. sticky_mark_sweep 为了加快 mark 与 sweep
  // 的过程, 假设上一次 GC 结束时的 mark bitmaps 在这一次 GC 时还是有
  // 效的, 即上一次 GC 时的对象都不会被释放: sticky_mark_sweep 只会扫
  // 描那些新增的对象
  // 为了优化 GC, collector 可以做一些假设, 但它只可以假设某对象被
  // mark, 而不能假设某对象没有被 mark.
  BindBitmaps();
  FindDefaultMarkBitmap();
  // Process dirty cards and add dirty cards to mod union tables.
  heap_->ProcessCards(timings_);
  MarkRoots(self);
  MarkReachableObjects();
    UpdateAndMarkModUnion();
    RecursiveMark();
1.2.5.2. PausePhase
PausePhase:
  if (IsConcurrent()):
    // CMS 需在在 PausePhase 使用 CardTable 进行重标记
    ReMarkRoots();
    // Scan dirty objects, this is only required if we are not doing concurrent GC.
    RecursiveMarkDirtyObjects(true, accounting::CardTable::kCardDirty);

  // SwapStacks 是将 allocation_stack_ 与 live_stack_ 交换, 从此以后,
  // 新分配的对象会被 push 到新的 allocation_stack_ 中, 而之前
  // allocation_stack_ 的内容现在位于 live_stack_ 中.
  // 后续 sticky_mark_sweep 会使用 live_stack_ 来查找最近新分配的对象
  heap_->SwapStacks();
1.2.5.3. ReclaimPhase
ReclaimPhase:
  ProcessReferences(self);
  Sweep(false);
  SwapBitmaps();
  UnBindBitmaps();

1.3. Heap

1.3.1. gc related

  1. collector_type_ heap 当前使用的 collector_type_, 进行 CollectGarbageInternal 时需要根据 collector_type_ 和 CollectGarbageInternal 的 gc_type 参数选择一个 collector
  2. background_collector_type_, foreground_collector_type_ TransitionCollector 时根据当前为 backgroup 或 foreground 把 collector_type_ 设置为 background_collector_type_ 或 foreground_collector_type_
  3. gc_plan_ gc_plan_ 是一个数组, 包含多个当前 collector 支持的 gc_type, 例如, 若 collector_type_ 为 MS, 则 gc_plan_ 为:

    [kGcTypeSticky, kGcTypePartial, kGcTypeFull]

    当 AllocateInternalWithGc 时, 会依次尝试使用 gc_plan_ 中指定的 gc_type 进行 CollectGarbageInternal, 以期望能尽量的进行轻量级的 gc

    另外, gc 成功时会记下本次使用的 gc_type 为 next_gc_type_, 当进行 ConcurrentGC 时, 它只会尝试 next_gc_type_ 及其之后的 gc_type

1.3.2. CollectGarbageInternal

CollectGarbageInternal 是进行 GC 的入口, 无论 ConcurrentGC 线程, 还是 AllocObject 导致的 GC, 或者强制进行的 GC, 最终都调用该接口.

// gc_type 代表要进行何种 gc: sticky, partial, full
// gc_cause 表示 gc 的原因: alloc, background(concurrent), explicit
// clear_soft_references: 是否清除 soft reference
// 由于 ART 一般使用 concurrent_gc_, 所以实际上这个函数的某些参数组合
// 是不存在的, 例如:
// 1. 若 gc_cause 为 concurrent, 则 gc_type 只能是 sticky 或 partial
// 2. 若 gc_cause 为 explicit, 则 gc_type 只能是 full
collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCause gc_cause,bool clear_soft_references):
  bool start_collect = false;
  // 获得 gc_complete_lock_, 防止有其他的 gc 同时进行
  // 若无法获得锁, 则等待 concurrent_gc 完成
  while (!start_collect):
    MutexLock mu(self, *gc_complete_lock_);
    if (!is_gc_running_)"
      is_gc_running_ = true;
      start_collect = true;
    if (!start_collect):
      WaitForConcurrentGcToComplete(self);

  // 根据 gc_type 选择 collector, mark_sweep_collectors_ 是与 Heap 一
  // 起初始化的, 包含六个 collector:
  // 1. full no concurrent
  // 2. partial no concurrent
  // 3. sticky no concurrent
  // 4. full concurrent
  // 5. partial concurrent
  // 6. sticky concurrent

  // 根据 gc_type 选择对应的 collector
  for (const auto& cur_collector : mark_sweep_collectors_) {
    if (cur_collector->IsConcurrent() == concurrent_gc_
        && cur_collector->GetGcType() == gc_type):
      collector = cur_collector;
      break;

  collector->clear_soft_references_ = clear_soft_references;
  collector->Run();

  RequestTrim(self);
  // Enqueue cleared references.
  reference_processor_->EnqueueClearedReferences(self);
  // Grow the heap so that we know when to perform the next GC.
  GrowForUtilization(collector, bytes_allocated_before_gc);

  FinishGC()
    is_gc_running_ = false;
    last_gc_type_ = gc_type;
    // Wake anyone who may have been waiting for the GC to complete.
    gc_complete_cond_->Broadcast(self);

1.3.3. DoPendingCollectorTransition

1.3.4. ConcurrentGC

1.4. Accounting

1.4.1. HeapBitmap

1.4.1.1. live_bitmap_
1.4.1.2. mark_bitmap_

1.4.2. ObjectStack

1.4.2.1. mark_stack_

mark_stack_ 与 MarkingPhase 有关

1.4.2.2. allocation_stack_ && live_stack_

sticky gc 有关

1.4.3. ModUnionTable

只有 ZygoteSpace 和 ImageSpace 有相应的 ModUnionTable, 用来支持 sticky 或 partial gc 时的 immune_region 的逻辑。

具体来说, mod_union_table 是一个 dirty card 的集合, 并且这个集合是持久的 (不像 card table 那样每次 gc 时被清空 (或者说 aged).

marking phase 的 ProcessCards 会负责将所有 dirty card (其值为 0x70) 减一, 同时将所有上次已经 age 过的 card (0x69) 置 0, 达到清理旧的 dirty card 的目的.然后, 对于 image_sapce 和 zygote_space, 将 dirty card 加入到相应的 mod_union_table 的 cleared_cards 中 (这些 mod_union_table 的 cleared_cards 的累积的, 不会因为 gc被清空).

所以, mod_union_table 本质上来源于 dirty_card, 其意义为"image_space(或 zygote_space) 中可能的指向 alloc_space 的对象"

marking phase 的后续阶段会通过 UpdateAndMarkModUnion 对上面那些可能的指向外部 space 的对象进行扫描 (scan object).

之所以说 mod_union_table 是为了和 sticky 或 partial gc 配合工作的, 是因为:

以 partial gc 为例, 其代码逻辑为:

  1. 将 image_space 和 zygote_space 标记为 immune_region (通过 BindBitmaps), 对于这些 immune_region, 其中的对象不经过扫描会直接被标记为 marked.
  2. 在 MarkReachableObjects 阶段, 通过 mod_union_table 找到那些可能的从 image_space (zygote_space) 引用到外部 space 的对象, 然后 scan 这些 object

通过这两步, 可以确保从 image_space (zygote_space) 出发, 能引用到的所有对象都被标记了. 如果没有 mod_union_table, 那么对 immune_region 完整的扫描是无法避免的.

— Q: 为什么需要 mod_union_table, 直接使用 card_table 不行么? A: card_table 每次 gc 都会被清空, 无法保存信息.

— Q: 为什么 alloc_space 没有 mod_union_table? sticky gc 也是会对 alloc_space 进行类似 immune_region 的操作的 … A: mod_union_table 的用处是持久的保存"从一个 space 引用其它 space" 的情况. 对于 alloc_space, 它只可能引用 image_space, zygote_space 或 LOS, 而对 alloc_space 进行immune_region 操作时, image_space, zygote_space 或 LOS 肯定也是 immune_region 的 …

— Q: 不对, 如果 alloc_space 引用 LOS 呢? A: sticky gc 会把 LOS 也 immune_region … (StickyMarkSweep::BindBitmaps)

1.4.4. CardTable

card table 是一个 write barrier, 用来标记 heap (continious space) 上哪些内存有改变.它有两个用处:

  1. 在 gc 的 marking phase, 构造 mod_union_table, 和 immune_region 配合进行初始的标记
  2. 在 gc 的 pause phase, ScanGrayObjects 会使用 card table 来进行重新标记 (但不会 age card table, card table 只会在 marking phase 被 age 一次)

marking phase 的 ProcessCards 会 age card table, 这里的 age 是指:

  1. 对于 dirty card, 其值为 0x70, age 会将它变为 0x69
  2. 对于 0x69, 即已经 age 过一次的 dirty card, age 会将它变为 0, 即 clean

之所以 card 有 0x70, 0x69, 0x0 这三种值 (而不是两种: dirty or clean), 是因为 card table 在一次 gc 会使用两次(markReachableObjects 与 scanGrayObjects), 为了避免重复的 scan dirty card, 所以用一个 0x69 加以区别,表示这个 card 之前已经 scan 过了.更具体来说, 0x69 这种 “准 dirty” card 是为了 sticky gc 准备的: sticky gc 时 alloc_space 没有 mod_union_table 这种东西, 可以用 0x69 类型的 card 来有限的代替 mod_union_table。

因为 LOS 不是连续的, 所以它没有 card table.

1.5. GC Daemon

注: android N 已经不存在 GC Daemon, concurrent gc 由 runtime 直接完成

dalvik 在初始化时会通过 gcDaemonThread 函数启动一个名为 "GC" 的线程负责 concurrent gc.

ART 中也存在类似的线程, 但它是一个 java daemon, 不是一个 native 的 thread.

StartDaemonThreads @ runtime.cc
  Daemons.start @ daemons.java
    ReferenceQueueDaemon.INSTANCE.start();
    FinalizerDaemon.INSTANCE.start();
    FinalizerWatchdogDaemon.INSTANCE.start();
    // 后面两个是 ART 新增的 daemon
    HeapTrimmerDaemon.INSTANCE.start();
    GCDaemon.INSTANCE.start();
      while (count == 0):
        wait();
      --count;
      VMRuntime.getRuntime().concurrentGC();
        Runtime::Current()->GetHeap()->ConcurrentGC(self);
          if (WaitForConcurrentGcToComplete(self) == collector::kGcTypeNone): @ heap.cc
            CollectGarbageInternal(next_gc_type_, kGcCauseBackground, false);

至于 GCDaemon 被唤醒的条件, 与 dalvik 是一致的: 当前内存低于某个阀值时, runtime 会通过 RequestConcurrentGC 唤醒 GCDaemon, 进行 concurrentGC.

Heap::AllocObject @ heap.cc
  if (UNLIKELY(static_cast<size_t>(num_bytes_allocated_) >= concurrent_start_bytes_)):
    RequestConcurrentGC(self);
      GCDaemon.requestGC @ Daemons.java
        ++count;
        notify();

2. allocator

2.1. Space

2.1.1. Overview

ART 的 space 类似于 dalvik 中的 HeapSource->heap[], 代表一块可以用来分配的内存区域.

2.1.1.1. dalvik heap

在 dalvik 中 HeapSource->heap[] 只有两项: 在 zygote 启动期间, heap[]只有一项 heap[0], 用来做为 zygote 启动时的 GC heap. zygote 启动完毕, fork 新的 java 进程前, zygote 使用的 heap[0] 会被移动到 heap[1], 然后 dalvik 会再初始化一块内存做为新的 heap[0], 用来做为后续 java 进程的 GC heap. 在任何时候, dalvik 都只使用 heap[0] 做为 GC heap 来分配对象.

zygote pre-fork 时初始化一个新的 GC heap, 主要是为了防止后续 java 进程再写那块旧的 GC heap: 这会导致大量的 copy on write 开销.

2.1.1.2. art space

在 ART 也有类似的机制, 不过 ART 设计的更好一些: 它抽象出几个 space:

  1. ImageSpace boot.art 的一部分 (0~image_size_部分) 会直接映射为 ImageSpace
  2. DlMallocSpace dalvik 上的 heap[] 相当于 DlMallocSpace, 即它是用 dlmalloc 来实现对象分配的.

    ART 上一般包含两个 DlMallocSpace:

    1. ZygoteSpace 这个 ZygoteSpace 保存在 continuous_spaces_ 数组中, zygote pre-fork 后不再使用它分配内存
    2. alloc_space_ 这个也保存在 continuous_spaces_ 数组中, zygote pre-fork 后会使用它来分配内存, 因为很常用, 所以有一个单独的 alloc_space_ 变量引用着它.
  3. LargeObjectSpace dalvik 中不存在类似概念, ART 使用 LargeObjectSpace 来分配大型 primitive 数组. 该对象有两个子类: FreeListSpace 和 LargeObjectMapSpace

另外, 根据 space 是否是连续的, 又分为 ContinuousSpace 和 DiscontinuousSpace, 所谓连续, 是指整个 space 是否有一个固定的大小, 所有空间都从这个固定大小的空间里分配. 例如, ImageSpace, DlMallocSpace 是 ContinuousSpace, 而 LargeObjectSpace 是 DiscontinuousSpace.

ContinuousSpace 因为空间有固定的大小,所以可以使用 SpaceBitmap 和 card table 来标识对象.

DiscontinuousSpace 因为空间大小不定, 无法使用 SpaceBitmap 来标识对象, 它使用 SpaceSetMap 来标识对象, SpaceSetMap 是使用 std::set 实现的 bitmap.

不同的 space 需要使用不同的 gc 算法 (collector), 例如:

  1. MallocSpace 会使用 CMS 算法 (Concurrent MarkSweep)
  2. BumpPointerSpace 只能使用 SS 算法 (SemiSpace), 因为 BumpPointerSpace 不支持单个对象的释放, 也没有live_bitmap 和 mark_bitmap, 无法使用 CMS 类算法
  3. RegionSpace 只能使用 CC 算法 (ConcurrentCopying)

2.1.2. Space hierachy

digraph "art::gc::space::Space"
{
  edge [fontname="Helvetica",fontsize="10",labelfontname="Helvetica",labelfontsize="10"];
  node [fontname="Helvetica",fontsize="10",shape=record];
  Node0 [label="art::gc::space::Space",height=0.2,width=0.4,color="black", fillcolor="grey75", style="filled", fontcolor="black"];
  Node0 -> Node1 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node1 [label="art::gc::space::Continuous\lSpace",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node1 -> Node3 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node3 [label="art::gc::space::MemMapSpace",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"]
  Node3 -> Node4 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node16 [label="art::gc::space::AllocSpace",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"]
  Node16 -> Node4 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node4 [label="art::gc::space::Continuous\lMemMapAllocSpace",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node4 -> Node6 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node14 [label="art::gc::space::BumpPointerSpace",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node4 -> Node14 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node15 [label="art::gc::space::RegionSpace",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node4 -> Node15 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node6 [label="art::gc::space::MallocSpace",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node6 -> Node7 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node7 [label="art::gc::space::DlMalloc\lSpace",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node6 -> Node8 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node8 [label="art::gc::space::RosAlloc\lSpace",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node3 -> Node9 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node9 [label="art::gc::space::ImageSpace",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node0 -> Node11 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node11 [label="art::gc::space::Discontinuous\lSpace",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node11 -> Node12 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node12 [label="art::gc::space::LargeObject\lSpace",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
  Node12 -> Node13 [dir="back",color="midnightblue",fontsize="10",style="solid",fontname="Helvetica"];
  Node13 [label="art::gc::space::LargeObject\lMapSpace",height=0.2,width=0.4,color="black", fillcolor="white", style="filled"];
}

art_space.png

2.1.3. ImageSpace

2.1.4. DlMallocSpace

2.1.5. RosAllocSpace

2.1.6. BumpPointerSpace

2.1.7. LargeObjectSpace

2.1.8. ZygoteSpace

在最初的 alloc_space_ 在 PreZygoteFork 时被转换为 ZygoteSpace 时, 会调用这个函数:

zygote_space->SetGcRetentionPolicy(space::kGcRetentionPolicyFullCollect);

2.1.9. GcRetentionPolicy

所谓的 GcRetentionPolicy 有三种选择:

  1. kGcRetentionPolicyNeverCollect, 这种 space 中的对象永远不会被 GC. 例如 ImageSpace
  2. kGcRetentionPolicyAlwaysCollect, 任何类型的 GC (Partial, Full) 都会 GC 这个 space 中的对象. 例如 LargeObjectSpace, DlMallocSpace 中的非 ZygoteSpace (例如 alloc_space_)
  3. kGcRetentionPolicyFullCollect, 只有在进行 FULL GC 时才对这个 space 的对象进行 GC. 例如 ZygoteSpace

GcRetentionPolicy 与 collector 的 GcType 有直接的关系

2.2. Allocator

2.3. AllocObject

Heap::AllocObject(Thread* self, mirror::Class* c, size_t byte_count) @ heap.cc
  // 对于大型的 primitive 数组, 会被分配到 large_object_space_ 中
  // art 有两种 large_object_space_ 实现: FreeListSpace 和 LargeObjectMapSpace, 前者在一块大的 mmap 空间上使用 free list 来分配
  // 后者用 mmap 直接分配. 目前只使用 LargeObjectMapSpace
  bool large_object_allocation = byte_count >= large_object_threshold_ && have_zygote_space_ && c->IsPrimitiveArray();
  if (UNLIKELY(large_object_allocation)):
    obj = Allocate(self, large_object_space_, byte_count, &bytes_allocated);
  else:
    obj = Allocate(self, alloc_space_, byte_count, &bytes_allocated);
      // ART 的 allocate 与 dalvik 类似, 也是一个多次尝试的过程, 中间伴随着
      // GC, 处理 SoftReferences, GC heap footprint grow 等过程.
      Object* Heap::Allocate(Thread* self, T* space, size_t alloc_size,...)
        ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated);
          if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))):
            // >>> IsOutOfMemoryOnAllocation
            if (UNLIKELY(new_footprint > growth_limit_)):
              return true;
            // ART 默认会使用 concurrent_gc, 所以 concurrent_gc_ 一般
            // 为真, 所以后面的代码基本不会跑到...而 dalvik 会跑到类似下面
            // 的代码. 所以 concurrent_gc_ 是不考虑 grow 参数的.
            if (!concurrent_gc_):
              if (!grow):
                return true;
              else:
                max_allowed_footprint_ = new_footprint;
            // <<< IsOutOfMemoryOnAllocation
            return NULL;

        if (ptr != NULL):
          return ptr;
        return AllocateInternalWithGc(self, space, alloc_size, bytes_allocated);
          last_gc = WaitForConcurrentGcToComplete(self);
          if (last_gc != collector::kGcTypeNone):
            ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated);
            if (ptr != NULL) return ptr;
          // last_gc 反映的是最近一次 concurrent gc 进行的是哪种 gc
          // 值越小表现越轻量级. 现在一共有三种 gc, 从轻到重依次是
          // sticky -> partial -> full
          // sticky 表示只回收上一次 gc 以来新分配的对象
          // partial 表示不回收 zygote space
          // full 表示完全回收所有对象
          // for 循环的起点是 last_gc+1, 表示使用比最近一次 concurrent gc
          // 使用的 gc type 更重量级的 collector
          for (size_t i = static_cast<size_t>(last_gc) + 1;
            i < static_cast<size_t>(collector::kGcTypeMax); ++i):
            // 尝试调用每一种 collector, 但事先需要满足一定的条件
            collector::GcType gc_type = static_cast<collector::GcType>(i);
            switch (gc_type):
              case collector::kGcTypeSticky:
                // 当可用内存很少时不使用 sticky gc
                run_gc = alloc_space_size > min_alloc_space_size_for_sticky_gc_ &&
                            alloc_space_->Capacity() - alloc_space_size >= min_remaining_space_for_sticky_gc_;
              case collector::kGcTypePartial:
                // 只有在有 zygote space 时使用 partial gc 才有意义
                run_gc = have_zygote_space_;
              case collector::kGcTypeFull:
                run_gc = true;
          // end for
          if run_gc:
            CollectGarbageInternal(gc_type, kGcCauseForAlloc, false);
            TryToAllocate(self, space, alloc_size, false, bytes_allocated);
          // 如果上一步仍没有分配到内存, 再调用一次 TryToAllocate, 但将
          // grow 置为 true. (但在 concurrent_gc_ 条件下, 这一参数是不起作用的...)
          ptr = TryToAllocate(self, space, alloc_size, true, bytes_allocated);
          // 若还是不行, 回收 SoftReferences
          CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true);
          TryToAllocate(self, space, alloc_size, true, bytes_allocated);

3. collector

4. space

5. gc_root

Author: [email protected]
Date: 2017-04-01 Sat 00:00
Last updated: 2024-05-08 Wed 15:25

知识共享许可协议