自学内容网 自学内容网

Android U ART young cc流程分析

 概述:

  众所周知jvm虚拟机为了提高内存回收效率,更高效的进行内存管理与回收,对堆内存进行了分代管理比如hotspot虚拟机的新生代,老年代。根据各代的特征( 新生代对象分配频繁而生存周期短,老年代生存周期长不需要频繁回收)来应用不同的内存回收算法

  art虚拟机也是如此,不过Android U上年轻代、老年代回收都采用的concurrent_copying回收算法,虚拟机定义了gc_type枚举类来定义不同的回收策略

enum GcType {
  kGcTypeNone,
  //粘性回收,只回收上次回收后分配的对象
  kGcTypeSticky,
  //部分回收,回收初zygote space之外的应用堆内存
  kGcTypePartial,
  //圈梁回收,应用堆包括zygote space
  kGcTypeFull,
  kGcTypeMax,
};

  枚举类型自上至下回收力度逐渐增大,本文主要分析kGcTypeSticky,其他回收类型后续文章再分析

 

 上图可以是在进行并发回收任务的时候(HeapTaskDaemon线程),如果没有找到合适的回收类型,会依次进行力度更大的回收

如果回收类型为 kGcTypeSticky,会启动年轻代回收器进行回收,那年轻代回收器到底是什么,都回收哪块内存就是我们今天要阐述的内容。

 并发拷贝cc算法的一般流程:初始化->标记->复制->回收

 art虚拟中并发拷贝算法的实现类concurrent_copying 中的gc全貌

void ConcurrentCopying::RunPhases() {
  CHECK(kUseBakerReadBarrier || kUseTableLookupReadBarrier);
  CHECK(!is_active_);
  is_active_ = true;
  Thread* self = Thread::Current();
  thread_running_gc_ = self;
  Locks::mutator_lock_->AssertNotHeld(self);
  {
    ReaderMutexLock mu(self, *Locks::mutator_lock_);
    InitializePhase(); //初始化阶段
    // In case of forced evacuation, all regions are evacuated and hence no
    // need to compute live_bytes.
    if (use_generational_cc_ && !young_gen_ && !force_evacuate_all_) {
      MarkingPhase(); 
    }
  }
  if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects) {
    // Switch to read barrier mark entrypoints before we gray the objects. This is required in case
    // a mutator sees a gray bit and dispatches on the entrypoint. (b/37876887).
    ActivateReadBarrierEntrypoints();
    // Gray dirty immune objects concurrently to reduce GC pause times. We re-process gray cards in
    // the pause.
    ReaderMutexLock mu(self, *Locks::mutator_lock_);
    GrayAllDirtyImmuneObjects();
  }
  FlipThreadRoots(); //threadroots切换阶段
  {
    ReaderMutexLock mu(self, *Locks::mutator_lock_);
    CopyingPhase(); //复制阶段
  }
  // Verify no from space refs. This causes a pause.
  if (kEnableNoFromSpaceRefsVerification) {
    TimingLogger::ScopedTiming split("(Paused)VerifyNoFromSpaceReferences", GetTimings());
    ScopedPause pause(this, false);
    CheckEmptyMarkStack();
    if (kVerboseMode) {
      LOG(INFO) << "Verifying no from-space refs";
    }
    VerifyNoFromSpaceReferences();
    if (kVerboseMode) {
      LOG(INFO) << "Done verifying no from-space refs";
    }
    CheckEmptyMarkStack();
  }
  {
    ReaderMutexLock mu(self, *Locks::mutator_lock_);
    ReclaimPhase();//回收阶段
  }
  FinishPhase();
  CHECK(is_active_);
  is_active_ = false;
  thread_running_gc_ = nullptr;
}

1. 初始化阶段


void ConcurrentCopying::InitializePhase() {
  TimingLogger::ScopedTiming split("InitializePhase", GetTimings());
  num_bytes_allocated_before_gc_ = static_cast<int64_t>(heap_->GetBytesAllocated());
 
  CheckEmptyMarkStack();
 

  BindBitmaps();

  if (use_generational_cc_ && !young_gen_) {
    region_space_bitmap_->Clear(ShouldEagerlyReleaseMemoryToOS());
  }
  mark_stack_mode_.store(ConcurrentCopying::kMarkStackModeThreadLocal, std::memory_order_release);
  MarkZygoteLargeObjects();
}


void ConcurrentCopying::BindBitmaps() {
  Thread* self = Thread::Current();
  WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
  // Mark all of the spaces we never collect as immune.
  for (const auto& space : heap_->GetContinuousSpaces()) {
    if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyNeverCollect ||
        space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect) {
      CHECK(space->IsZygoteSpace() || space->IsImageSpace());
      immune_spaces_.AddSpace(space); //imagespace zygote space添加到不动space
    } else {
      CHECK(!space->IsZygoteSpace());
      CHECK(!space->IsImageSpace());
      CHECK(space == region_space_ || space == heap_->non_moving_space_);
      if (use_generational_cc_) {
        if (space == region_space_) {
          region_space_bitmap_ = region_space_->GetMarkBitmap();
        } else if (young_gen_ && space->IsContinuousMemMapAllocSpace()) {
          DCHECK_EQ(space->GetGcRetentionPolicy(), space::kGcRetentionPolicyAlwaysCollect);
          space->AsContinuousMemMapAllocSpace()->BindLiveToMarkBitmap();
        }
        if (young_gen_) {
          // Age all of the cards for the region space so that we know which evac regions to scan.
          heap_->GetCardTable()->ModifyCardsAtomic(space->Begin(),
                                                   space->End(),
                                                   AgeCardVisitor(),
                                                   VoidFunctor());
        } else {
          heap_->GetCardTable()->ClearCardRange(space->Begin(), space->Limit());
        }
      } 
    }
  }
  if (use_generational_cc_ && young_gen_) {
    for (const auto& space : GetHeap()->GetDiscontinuousSpaces()) {
      CHECK(space->IsLargeObjectSpace());
      space->AsLargeObjectSpace()->CopyLiveToMarked();
    }
  }
}

2.标记阶段

void ConcurrentCopying::FlipThreadRoots() {
  TimingLogger::ScopedTiming split("FlipThreadRoots", GetTimings());
  if (kVerboseMode || heap_->dump_region_info_before_gc_) {
    LOG(INFO) << "time=" << region_space_->Time();
    region_space_->DumpNonFreeRegions(LOG_STREAM(INFO));
  }
  Thread* self = Thread::Current();
  Locks::mutator_lock_->AssertNotHeld(self);
  ThreadFlipVisitor thread_flip_visitor(this, heap_->use_tlab_);
  FlipCallback flip_callback(this);

  Runtime::Current()->GetThreadList()->FlipThreadRoots(
      &thread_flip_visitor, &flip_callback, this, GetHeap()->GetGcPauseListener());

  is_asserting_to_space_invariant_ = true;
  QuasiAtomic::ThreadFenceForConstructor();  // TODO: Remove?
  if (kVerboseMode) {
    LOG(INFO) << "time=" << region_space_->Time();
    region_space_->DumpNonFreeRegions(LOG_STREAM(INFO));
    LOG(INFO) << "GC end of FlipThreadRoots";
  }
}

hotspot虚拟机中8:1比例划分对新生代堆空间进行划分,大空间分配的对象往往朝生夕死,当内存被回收后会被拷贝到小空间,art虚拟机虽然也采用分代策略但是采用了不同的机制

art虚拟机中应用分配的普通对象会分配在main_space(region_space类实现)空间,而>12k的string类型或者原始数组类型会分配到large_object_space空间

  enum class RegionType : uint8_t {
    kRegionTypeAll,              // All types.
    kRegionTypeFromSpace,        // From-space. To be evacuated.
    kRegionTypeUnevacFromSpace,  // Unevacuated from-space. Not to be evacuated.
    kRegionTypeToSpace,          // To-space.
    kRegionTypeNone,             // None.
  };

FlipThreadRoot 会进行

a. region_space类型切换

regionspace由一个个固定大小的region组成,分配对象的region类型为kRegionTypeToSpace,而当需要内存回收的时候会切换为kRegionTypeFromSpace(拷贝从from拷贝的to)代码位置在FlipCallback->setFromSpace

b. 标记gcroot

 art虚拟机中的gcroot-CSDN博客可以参考这篇文章,被标记的对象会被压入到mark_stack(PushOntoMarkStack)中

那kGcTypeSticky回收策略是怎么做到只回收上次分配的对象的呢?

inline bool RegionSpace::Region::ShouldBeEvacuated(EvacMode evac_mode) {

  if (IsLarge()) {
    return false;
  }
  if (UNLIKELY(evac_mode == kEvacModeForceAll)) {
    return true;
  }
  DCHECK(IsAllocated());
  if (is_newly_allocated_) {

    return true;
  } else if (evac_mode == kEvacModeLivePercentNewlyAllocated) {
    bool is_live_percent_valid = (live_bytes_ != static_cast<size_t>(-1));
    if (is_live_percent_valid) {
    
      return live_bytes_ * 100U < kEvacuateLivePercentThreshold * bytes_allocated;
    }
  }
  return false;
}

当空间是新分配或者存活对象比例低于75%,才会设置成kRegionTypeFromSpace,即需要被复制的空间

3.复制阶段

CopyingPhase(); //复制阶段, 

for (auto& space : immune_spaces_.GetSpaces()) {
      DCHECK(space->IsImageSpace() || space->IsZygoteSpace());
      accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
      accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space);
      ImmuneSpaceScanObjVisitor visitor(this);
      if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects && table != nullptr) {
        table->VisitObjects(ImmuneSpaceScanObjVisitor::Callback, &visitor);
      } else {
        WriterMutexLock rmu(Thread::Current(), *Locks::heap_bitmap_lock_);
        card_table->Scan<false>(
            live_bitmap,
            space->Begin(),
            space->Limit(),
            visitor,
            accounting::CardTable::kCardDirty - 1);
      }
}

需要把immuse space(zygote space,imagespace)中的存活对象进行拷贝,ImageSpace中的对象可以直接标记,因为映射dex 等Image文件所以可以直接被认为是gcroot

广度优先遍历mark_stack,把mark_stack中的对象及其引用拷贝到toSpace中,同时

4.回收阶段

回收fromspace(非stick会回收largetObjecSpace)

5.结束


原文地址:https://blog.csdn.net/congqingbin/article/details/144002335

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!