Android中GC的触发时机和条件

本文分析基于Android R(11)源码

Java对象的创建由Allocator负责,回收由Collector负责。从Android O开始,对于前台应用默认的GC Collector是CC(Concurrent Copying) Collector,与之相匹配的Allocator则是Region-based Bump Pointer Allocator(with TLAB)。

本文不打算讨论CC Collector和Region TLAB的细节和实现,一是因为这些内容受众较少,二是因为我自己还在慢慢摸索和吃透其中的细节,可能日后会专门成文介绍他们。

除去繁杂的细节介绍,本文希望回答一个简单的问题:

对前台应用而言,GC到底会在何时触发?

art/runtime/gc/gc_cause.h

25 // What caused the GC?
26 enum GcCause {
27   // Invalid GC cause used as a placeholder.
28   kGcCauseNone,
29   // GC triggered by a failed allocation. Thread doing allocation is blocked waiting for GC before
30   // retrying allocation.
31   kGcCauseForAlloc,
32   // A background GC trying to ensure there is free memory ahead of allocations.
33   kGcCauseBackground,
34   // An explicit System.gc() call.
35   kGcCauseExplicit,
36   // GC triggered for a native allocation when NativeAllocationGcWatermark is exceeded.
37   // (This may be a blocking GC depending on whether we run a non-concurrent collector).
38   kGcCauseForNativeAlloc,
39   // GC triggered for a collector transition.
40   kGcCauseCollectorTransition,
41   // Not a real GC cause, used when we disable moving GC (currently for GetPrimitiveArrayCritical).
42   kGcCauseDisableMovingGc,
43   // Not a real GC cause, used when we trim the heap.
44   kGcCauseTrim,
45   // Not a real GC cause, used to implement exclusion between GC and instrumentation.
46   kGcCauseInstrumentation,
47   // Not a real GC cause, used to add or remove app image spaces.
48   kGcCauseAddRemoveAppImageSpace,
49   // Not a real GC cause, used to implement exclusion between GC and debugger.
50   kGcCauseDebugger,
51   // GC triggered for background transition when both foreground and background collector are CMS.
52   kGcCauseHomogeneousSpaceCompact,
53   // Class linker cause, used to guard filling art methods with special values.
54   kGcCauseClassLinker,
55   // Not a real GC cause, used to implement exclusion between code cache metadata and GC.
56   kGcCauseJitCodeCache,
57   // Not a real GC cause, used to add or remove system-weak holders.
58   kGcCauseAddRemoveSystemWeakHolder,
59   // Not a real GC cause, used to prevent hprof running in the middle of GC.
60   kGcCauseHprof,
61   // Not a real GC cause, used to prevent GetObjectsAllocated running in the middle of GC.
62   kGcCauseGetObjectsAllocated,
63   // GC cause for the profile saver.
64   kGcCauseProfileSaver,
65   // GC cause for running an empty checkpoint.
66   kGcCauseRunEmptyCheckpoint,
67 };

根据GcCause可知,可以触发GC的条件还是很多的。对于开发者而言,常见的是其中三种:

  • GcCauseForAlloc:通过new分配新对象时,堆中剩余空间(普通应用默认上限为256M,声明largeHeap的应用为512M)不足,因此需要先进行GC。这种情况会导致当前线程阻塞。

  • GcCauseExplicit:当应用调用系统API System.gc()时,会产生一次GC动作。

  • GcCauseBackground:后台GC,这里的“后台”并不是指应用切到后台才会执行的GC,而是GC在运行时基本不会影响其他线程的执行,所以也可以理解为并发GC。相比于前两种GC,后台GC出现的更多也更加隐秘,因此值得详细介绍。下文讲述的全是这种GC。

Java堆的实际大小起起伏伏,影响的因素无非是分配和回收。分配的过程是离散且频繁的,它来自于不同的工作线程,而且可能每次只分配一小块区域。回收的过程则是统一且偶发的,它由HeapTaskDaemon线程执行,在GC的多个阶段中都采用并发算法,因此不会暂停工作线程(实际上会暂停很短一段时间)。

当我们在Java代码中通过new分配对象时,虚拟机会调用AllocObjectWithAllocator来执行真实的分配。在每一次成功分配Java对象后,都会去检测是否需要进行下一次GC,这就是GcCauseBackground GC的触发时机。

art/runtime/gc/heap-inl.h

44 template <bool kInstrumented, bool kCheckLargeObject, typename PreFenceVisitor>
45 inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self,
46                                                       ObjPtr<mirror::Class> klass,
47                                                       size_t byte_count,
48                                                       AllocatorType allocator,
49                                                       const PreFenceVisitor& pre_fence_visitor) {
...
243   // IsGcConcurrent() isn't known at compile time so we can optimize by not checking it for
244   // the BumpPointer or TLAB allocators. This is nice since it allows the entire if statement to be
245   // optimized out. And for the other allocators, AllocatorMayHaveConcurrentGC is a constant since
246   // the allocator_type should be constant propagated.
247   if (AllocatorMayHaveConcurrentGC(allocator) && IsGcConcurrent()) {
248     // New_num_bytes_allocated is zero if we didn't update num_bytes_allocated_.
249     // That's fine.
250     CheckConcurrentGCForJava(self, new_num_bytes_allocated, &obj);       <===================== This line
251   }
252   VerifyObject(obj);
253   self->VerifyStack();
254   return obj.Ptr();
255 }
467 inline void Heap::CheckConcurrentGCForJava(Thread* self,
468                                     size_t new_num_bytes_allocated,
469                                     ObjPtr<mirror::Object>* obj) {
470   if (UNLIKELY(ShouldConcurrentGCForJava(new_num_bytes_allocated))) {    <======================= This line
471     RequestConcurrentGCAndSaveObject(self, false /* force_full */, obj);
472   }
473 }
460 inline bool Heap::ShouldConcurrentGCForJava(size_t new_num_bytes_allocated) {
461   // For a Java allocation, we only check whether the number of Java allocated bytes excceeds a
462   // threshold. By not considering native allocation here, we (a) ensure that Java heap bounds are
463   // maintained, and (b) reduce the cost of the check here.
464   return new_num_bytes_allocated >= concurrent_start_bytes_;            <======================== This line
465 }
466 

触发的条件需要满足一个判断,如果new_num_bytes_allocated(所有已分配的字节数,包括此次新分配的对象) >= concurrent_start_bytes_(下一次GC触发的阈值),那么就请求一次新的GC。new_num_bytes_alloated是当前分配时计算的,concurrent_start_bytes_是上一次GC结束时计算的。以下将分别介绍这两个值的计算过程和背后的设计思想。

1. new_num_bytes_allocated的计算过程

art/runtime/gc/heap-inl.h

44 template <bool kInstrumented, bool kCheckLargeObject, typename PreFenceVisitor>
45 inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self,
46                                                       ObjPtr<mirror::Class> klass,
47                                                       size_t byte_count,
48                                                       AllocatorType allocator,
49                                                       const PreFenceVisitor& pre_fence_visitor) {
...
83   size_t new_num_bytes_allocated = 0;
84   {
85     // Do the initial pre-alloc
86     pre_object_allocated();
...
107     if (IsTLABAllocator(allocator)) {
108       byte_count = RoundUp(byte_count, space::BumpPointerSpace::kAlignment);
109     }
110     // If we have a thread local allocation we don't need to update bytes allocated.
111     if (IsTLABAllocator(allocator) && byte_count <= self->TlabSize()) {
112       obj = self->AllocTlab(byte_count);
113       DCHECK(obj != nullptr) << "AllocTlab can't fail";
114       obj->SetClass(klass);
115       if (kUseBakerReadBarrier) {
116         obj->AssertReadBarrierState();
117       }
118       bytes_allocated = byte_count;
119       usable_size = bytes_allocated;
120       no_suspend_pre_fence_visitor(obj, usable_size);
121       QuasiAtomic::ThreadFenceForConstructor();
122     } else if (
123         !kInstrumented && allocator == kAllocatorTypeRosAlloc &&
124         (obj = rosalloc_space_->AllocThreadLocal(self, byte_count, &bytes_allocated)) != nullptr &&
125        LIKELY(obj != nullptr)) {
126      DCHECK(!is_running_on_memory_tool_);
127      obj->SetClass(klass);
128      if (kUseBakerReadBarrier) {
129        obj->AssertReadBarrierState();
130      }
131      usable_size = bytes_allocated;
132      no_suspend_pre_fence_visitor(obj, usable_size);
133      QuasiAtomic::ThreadFenceForConstructor();
134    } else {
135      // Bytes allocated that includes bulk thread-local buffer allocations in addition to direct
136      // non-TLAB object allocations.
137      size_t bytes_tl_bulk_allocated = 0u;
138      obj = TryToAllocate<kInstrumented, false>(self, allocator, byte_count, &bytes_allocated,
139                                                &usable_size, &bytes_tl_bulk_allocated);
140      if (UNLIKELY(obj == nullptr)) {
141         // AllocateInternalWithGc can cause thread suspension, if someone instruments the
142         // entrypoints or changes the allocator in a suspend point here, we need to retry the
143         // allocation. It will send the pre-alloc event again.
144         obj = AllocateInternalWithGc(self,
145                                      allocator,
146                                      kInstrumented,
147                                      byte_count,
148                                      &bytes_allocated,
149                                      &usable_size,
150                                      &bytes_tl_bulk_allocated,
151                                      &klass);
152         if (obj == nullptr) {
153           // The only way that we can get a null return if there is no pending exception is if the
154           // allocator or instrumentation changed.
155           if (!self->IsExceptionPending()) {
156             // Since we are restarting, allow thread suspension.
157             ScopedAllowThreadSuspension ats;
158             // AllocObject will pick up the new allocator type, and instrumented as true is the safe
159             // default.
160             return AllocObject</*kInstrumented=*/true>(self,
161                                                        klass,
162                                                        byte_count,
163                                                        pre_fence_visitor);
164           }
165           return nullptr;
166         }
167       }
168       DCHECK_GT(bytes_allocated, 0u);
169       DCHECK_GT(usable_size, 0u);
170       obj->SetClass(klass);
171       if (kUseBakerReadBarrier) {
172         obj->AssertReadBarrierState();
173       }
174       if (collector::SemiSpace::kUseRememberedSet &&
175           UNLIKELY(allocator == kAllocatorTypeNonMoving)) {
176         // (Note this if statement will be constant folded away for the fast-path quick entry
177         // points.) Because SetClass() has no write barrier, the GC may need a write barrier in the
178         // case the object is non movable and points to a recently allocated movable class.
179         WriteBarrier::ForFieldWrite(obj, mirror::Object::ClassOffset(), klass);
180       }
181       no_suspend_pre_fence_visitor(obj, usable_size);
182       QuasiAtomic::ThreadFenceForConstructor();
183       if (bytes_tl_bulk_allocated > 0) {
184         size_t num_bytes_allocated_before =
185             num_bytes_allocated_.fetch_add(bytes_tl_bulk_allocated, std::memory_order_relaxed);
186         new_num_bytes_allocated = num_bytes_allocated_before + bytes_tl_bulk_allocated;
187         // Only trace when we get an increase in the number of bytes allocated. This happens when
188         // obtaining a new TLAB and isn't often enough to hurt performance according to golem.
189         if (region_space_) {
190           // With CC collector, during a GC cycle, the heap usage increases as
191           // there are two copies of evacuated objects. Therefore, add evac-bytes
192           // to the heap size. When the GC cycle is not running, evac-bytes
193           // are 0, as required.
194           TraceHeapSize(new_num_bytes_allocated + region_space_->EvacBytes());
195         } else {
196           TraceHeapSize(new_num_bytes_allocated);
197         }
198       }
199     }
200   }
...
243   // IsGcConcurrent() isn't known at compile time so we can optimize by not checking it for
244   // the BumpPointer or TLAB allocators. This is nice since it allows the entire if statement to be
245   // optimized out. And for the other allocators, AllocatorMayHaveConcurrentGC is a constant since
246   // the allocator_type should be constant propagated.
247   if (AllocatorMayHaveConcurrentGC(allocator) && IsGcConcurrent()) {
248     // New_num_bytes_allocated is zero if we didn't update num_bytes_allocated_.
249     // That's fine.
250     CheckConcurrentGCForJava(self, new_num_bytes_allocated, &obj);
251   }
...
255 }

AllocObjectWithAllocator的实际分配可以分为三条分支,但如果限定为Region-based Bump Pointer Allocator,则只剩两条分支:

  1. 如果当前线程TLAB区域的剩余空间可以容纳下这次分配的对象,则在TLAB区域中直接分配。分配算法采用Bump Pointer的方式,仅仅更新已分配区域的游标,简单高效。

    art/runtime/thread-inl.h

    307 inline mirror::Object* Thread::AllocTlab(size_t bytes) {
    308   DCHECK_GE(TlabSize(), bytes);
    309   ++tlsPtr_.thread_local_objects;
    310   mirror::Object* ret = reinterpret_cast<mirror::Object*>(tlsPtr_.thread_local_pos);
    311   tlsPtr_.thread_local_pos += bytes;
    312   return ret;
    313 }
    

    在这种情况下,new_num_bytes_allocated为0,表明Java堆的已使用区域并没有增大。这是因为TLAB在创建之初,它的大小已经计入了num_bytes_allocated_,所以这次虽然分配了新的对象,但num_bytes_allocated_没必要增加。

    那么紧接着就来了一个问题:为什么TLAB在创建之初就要将大小计入num_bytes_allocated_呢?可是此时TLAB明明还没有被使用。这实际上是一个空间换时间的策略。以下是当时这笔改动的commit message,通过事先将大小计入num_bytes_allocated_从而不必要每次分配都更新它,减少针对num_bytes_allocated_的原子操作,提高性能。代价就是会导致num_bytes_allocated_略大于真实使用的字节数。

    [Commit Message]

    Faster TLAB allocator.New TLAB allocator doesn't increment bytes allocated until we allocate
    a new TLAB. This increases allocation performance by avoiding a CAS.MemAllocTest:
    Before GSS TLAB: 3400ms.
    After GSS TLAB: 2750ms.Bug: 9986565Change-Id: I1673c27555330ee90d353b98498fa0e67bd57fad
    Author: mathieuc@google.com
    Date: 2014-07-12 05:18
    
  2. 如果当前线程TLAB区域的剩余空间无法容纳下这次分配的对象,则为当前线程创建一个新的TLAB。在这种情况下,新分配出来的TLAB大小需要计入num_bytes_allocated_,因此new_num_bytes_allocated = num_bytes_allocated_before + bytes_tl_bulk_allocated。

2. concurrent_start_bytes_的计算过程

art/runtime/gc/heap.cc

2573 collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type,
2574                                                GcCause gc_cause,
2575                                                bool clear_soft_references) {
...
2671   collector->Run(gc_cause, clear_soft_references || runtime->IsZygote());
2672   IncrementFreedEver();
2673   RequestTrim(self);
2674   // Collect cleared references.
2675   SelfDeletingTask* clear = reference_processor_->CollectClearedReferences(self);
2676   // Grow the heap so that we know when to perform the next GC.
2677   GrowForUtilization(collector, bytes_allocated_before_gc);
2678   LogGC(gc_cause, collector);
2679   FinishGC(self, gc_type);
2680   // Actually enqueue all cleared references. Do this after the GC has officially finished since
2681   // otherwise we can deadlock.
2682   clear->Run(self);
2683   clear->Finalize();
2684   // Inform DDMS that a GC completed.
2685   Dbg::GcDidFinish();
2686 
2687   old_native_bytes_allocated_.store(GetNativeBytes());
2688 
2689   // Unload native libraries for class unloading. We do this after calling FinishGC to prevent
2690   // deadlocks in case the JNI_OnUnload function does allocations.
2691   {
2692     ScopedObjectAccess soa(self);
2693     soa.Vm()->UnloadNativeLibraries();
2694   }
2695   return gc_type;
2696 }

CollectGarbageInternal是HeapTaskDaemon线程执行GC时需要调用的函数。其中2671行将执行真正的GC,而concurrent_start_bytes_的计算则在2677行的GrowForUtilization函数中。

art/runtime/gc/heap.cc

3514 void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran,
3515                               size_t bytes_allocated_before_gc) {
3516   // We know what our utilization is at this moment.
3517   // This doesn't actually resize any memory. It just lets the heap grow more when necessary.
3518   const size_t bytes_allocated = GetBytesAllocated();
3519   // Trace the new heap size after the GC is finished.
3520   TraceHeapSize(bytes_allocated);
3521   uint64_t target_size, grow_bytes;
3522   collector::GcType gc_type = collector_ran->GetGcType();
3523   MutexLock mu(Thread::Current(), process_state_update_lock_);
3524   // Use the multiplier to grow more for foreground.
3525   const double multiplier = HeapGrowthMultiplier();
3526   if (gc_type != collector::kGcTypeSticky) {
3527     // Grow the heap for non sticky GC.
3528     uint64_t delta = bytes_allocated * (1.0 / GetTargetHeapUtilization() - 1.0);
3529     DCHECK_LE(delta, std::numeric_limits<size_t>::max()) << "bytes_allocated=" << bytes_allocated
3530         << " target_utilization_=" << target_utilization_;
3531     grow_bytes = std::min(delta, static_cast<uint64_t>(max_free_));
3532     grow_bytes = std::max(grow_bytes, static_cast<uint64_t>(min_free_));
3533     target_size = bytes_allocated + static_cast<uint64_t>(grow_bytes * multiplier);
3534     next_gc_type_ = collector::kGcTypeSticky;
3535   } else {
...
3562     // If we have freed enough memory, shrink the heap back down.
3563     const size_t adjusted_max_free = static_cast<size_t>(max_free_ * multiplier);
3564     if (bytes_allocated + adjusted_max_free < target_footprint) {
3565       target_size = bytes_allocated + adjusted_max_free;
3566       grow_bytes = max_free_;
3567     } else {
3568       target_size = std::max(bytes_allocated, target_footprint);
3569       // The same whether jank perceptible or not; just avoid the adjustment.
3570       grow_bytes = 0;
3571     }
3572   }
3573   CHECK_LE(target_size, std::numeric_limits<size_t>::max());
3574   if (!ignore_target_footprint_) {
3575     SetIdealFootprint(target_size);
...
3585     if (IsGcConcurrent()) {
3586       const uint64_t freed_bytes = current_gc_iteration_.GetFreedBytes() +
3587           current_gc_iteration_.GetFreedLargeObjectBytes() +
3588           current_gc_iteration_.GetFreedRevokeBytes();
3589       // Bytes allocated will shrink by freed_bytes after the GC runs, so if we want to figure out
3590       // how many bytes were allocated during the GC we need to add freed_bytes back on.
3591       CHECK_GE(bytes_allocated + freed_bytes, bytes_allocated_before_gc);
3592       const size_t bytes_allocated_during_gc = bytes_allocated + freed_bytes -
3593           bytes_allocated_before_gc;
3594       // Calculate when to perform the next ConcurrentGC.
3595       // Estimate how many remaining bytes we will have when we need to start the next GC.
3596       size_t remaining_bytes = bytes_allocated_during_gc;
3597       remaining_bytes = std::min(remaining_bytes, kMaxConcurrentRemainingBytes);
3598       remaining_bytes = std::max(remaining_bytes, kMinConcurrentRemainingBytes);
3599       size_t target_footprint = target_footprint_.load(std::memory_order_relaxed);
3600       if (UNLIKELY(remaining_bytes > target_footprint)) {
3601         // A never going to happen situation that from the estimated allocation rate we will exceed
3602         // the applications entire footprint with the given estimated allocation rate. Schedule
3603         // another GC nearly straight away.
3604         remaining_bytes = std::min(kMinConcurrentRemainingBytes, target_footprint);
3605       }
3606       DCHECK_LE(target_footprint_.load(std::memory_order_relaxed), GetMaxMemory());
3607       // Start a concurrent GC when we get close to the estimated remaining bytes. When the
3608       // allocation rate is very high, remaining_bytes could tell us that we should start a GC
3609       // right away.
3610       concurrent_start_bytes_ = std::max(target_footprint - remaining_bytes, bytes_allocated);
3611     }
3612   }
3613 }

concurrent_start_bytes_的计算分为两个步骤:

  1. 计算出target_size,一个仅具有指导意义的最大可分配字节数。

  2. 根据target_size计算出下一次GC的触发水位concurrent_start_bytes_。

2.1 target_size的计算过程

2.1.1 Sticky GC

kGcTypeSticky是分代GC下的一种GC类型,表示只针对两次GC时间内新分配的对象进行回收,也可以理解为Young-generation GC。如果gc_type为kGcTypeSticky,则执行如下过程:

art/runtime/gc/heap.cc

3562     // If we have freed enough memory, shrink the heap back down.
3563     const size_t adjusted_max_free = static_cast<size_t>(max_free_ * multiplier);
3564     if (bytes_allocated + adjusted_max_free < target_footprint) {
3565       target_size = bytes_allocated + adjusted_max_free;
3566       grow_bytes = max_free_;
3567     } else {
3568       target_size = std::max(bytes_allocated, target_footprint);
3569       // The same whether jank perceptible or not; just avoid the adjustment.
3570       grow_bytes = 0;
3571     }

max_free_的本意是target_size与已分配内存间可允许的最大差异,差异过小会导致GC频繁,差异过大则又会延迟下一次GC的到来,目前很多设备将这个值设为8M,min_free_设为512K。其实针对RAM超过6G的大内存设备,Google建议可以提高min_free_,用空间换时间获取更好的GC性能。multiplier的引入主要是为了优化前台应用,默认的前台multipiler为2,这样可以在下次GC前有更多的空间分配对象。以下是引入multipiler的代码的commit message,增大free的空间自然就降低了利用率。

[Commit Message]

Decrease target utilization for foreground apps.GC time in FormulaEvaluationActions.EvaluateAndApplyChanges goes from
26.1s to 23.2s. Benchmark score goes down ~50 in
FormulaEvaluationActions.EvaluateAndApplyChanges, and up ~50 in
GenericCalcActions.MemAllocTest.Bug: 8788501
Change-Id: I412af1205f8b67e70a12237c990231ea62167bc0
Author: mathieuc@google.com
Date: 2014-04-17 03:37
  1. 当bytes_allocated + adjusted_max_free < target_footprint时,说明这次GC释放了很多空间,因此可以适当地降低下次GC的触发水位。

  2. 如果bytes_allocated + adjusted_max_free ≥ target_footprint,则取target_footprint和bytes_allocated中的较大值作为target_size。

    这种情况这次GC释放的空间不多。当target_footprint较大时,即便bytes_allocated逼近target_footprint也不增大target_size,是因为当前GC为Sticky GC(又可理解为Young-generation GC),如果它释放的空间不多,接下来还可以采用Full GC来更彻底地回收。换言之,只有等Full GC回收完,才能决定将GC的水位提升,因为这时已经尝试了所有的回收策略。

    当bytes_allocated较大时,说明在GC过程中新申请的对象空间大于GC释放的空间(因为并发,所以申请和释放可以同步进行)。这样一来,最终计算的水位值将会小于bytes_allocated,那岂不是下一次调用new分配对象时必然会阻塞?实则不然。因为不论是target_size还是concurrent_start_bytes_,他们都只有指导意义而无法实际限制堆内存的分配。对于CC Collector而言,唯一限制堆内存分配的只有growth_limit_。不过水位值小于bytes_allocated倒是会使得下一次对象分配成功后立马触发一轮新的GC。

2.1.2 Non-sticky GC

art/runtime/gc/heap.cc

3526   if (gc_type != collector::kGcTypeSticky) {
3527     // Grow the heap for non sticky GC.
3528     uint64_t delta = bytes_allocated * (1.0 / GetTargetHeapUtilization() - 1.0);
3529     DCHECK_LE(delta, std::numeric_limits<size_t>::max()) << "bytes_allocated=" << bytes_allocated
3530         << " target_utilization_=" << target_utilization_;
3531     grow_bytes = std::min(delta, static_cast<uint64_t>(max_free_));
3532     grow_bytes = std::max(grow_bytes, static_cast<uint64_t>(min_free_));
3533     target_size = bytes_allocated + static_cast<uint64_t>(grow_bytes * multiplier);
3534     next_gc_type_ = collector::kGcTypeSticky;
3535   } 

首先会根据目标的利用率计算出新的delta,然后将delta与min_free_和max_free_进行比较,使得最终的grow_bytes落在[min_free_,max_free_]之间。target_size的计算还需考虑multipiler的影响,这样会降低前台应用的堆利用率,从而留有更多空间进行分配(降低GC的频率,代价就是内存资源向前台应用倾斜)。以下是一部手机的堆配置,其中数值可做参考。

2.2 concurrent_start_bytes_的计算

art/runtime/gc/heap.cc

3585     if (IsGcConcurrent()) {
3586       const uint64_t freed_bytes = current_gc_iteration_.GetFreedBytes() +
3587           current_gc_iteration_.GetFreedLargeObjectBytes() +
3588           current_gc_iteration_.GetFreedRevokeBytes();
3589       // Bytes allocated will shrink by freed_bytes after the GC runs, so if we want to figure out
3590       // how many bytes were allocated during the GC we need to add freed_bytes back on.
3591       CHECK_GE(bytes_allocated + freed_bytes, bytes_allocated_before_gc);
3592       const size_t bytes_allocated_during_gc = bytes_allocated + freed_bytes -
3593           bytes_allocated_before_gc;
3594       // Calculate when to perform the next ConcurrentGC.
3595       // Estimate how many remaining bytes we will have when we need to start the next GC.
3596       size_t remaining_bytes = bytes_allocated_during_gc;
3597       remaining_bytes = std::min(remaining_bytes, kMaxConcurrentRemainingBytes);
3598       remaining_bytes = std::max(remaining_bytes, kMinConcurrentRemainingBytes);
3599       size_t target_footprint = target_footprint_.load(std::memory_order_relaxed);
3600       if (UNLIKELY(remaining_bytes > target_footprint)) {
3601         // A never going to happen situation that from the estimated allocation rate we will exceed
3602         // the applications entire footprint with the given estimated allocation rate. Schedule
3603         // another GC nearly straight away.
3604         remaining_bytes = std::min(kMinConcurrentRemainingBytes, target_footprint);
3605       }
3606       DCHECK_LE(target_footprint_.load(std::memory_order_relaxed), GetMaxMemory());
3607       // Start a concurrent GC when we get close to the estimated remaining bytes. When the
3608       // allocation rate is very high, remaining_bytes could tell us that we should start a GC
3609       // right away.
3610       concurrent_start_bytes_ = std::max(target_footprint - remaining_bytes, bytes_allocated);
3611     }

首先需要计算出在GC过程中新分配的对象大小,记为bytes_allocated_during_gc。然后将它与kMinConcurrentRemainingBytes和kMaxConcurrentRemainingBytes进行比较,使得最终的grow_bytes落在[kMinConcurrentRemainingBytes,kMaxConcurrentRemainingBytes]之间。

art/runtime/gc/heap.cc

108 // Minimum amount of remaining bytes before a concurrent GC is triggered.
109 static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB;
110 static constexpr size_t kMaxConcurrentRemainingBytes = 512 * KB;

最终concurrent_start_bytes_的计算如下。之所以需要用target_footprint减去remaining_bytes,是因为在理论意义上,target_footprint_代表当前堆的最大可分配字节数。而由于是同步GC,回收的过程中可能会有其他线程依然在分配。所以为了保证本次GC的顺利进行,需要将这段时间分配的内存空间预留出来。

art/runtime/gc/heap.cc

concurrent_start_bytes_ = std::max(target_footprint - remaining_bytes, bytes_allocated);

不过需要注意的是,上面阐述的理由仅局限在理论意义上,就像target_footprint_和concurrent_start_bytes_只具有指导意义一样。所以即便下一次GC过程中分配的内存超过了预留的空间,也并不会出现内存分配不出来而等待的情况。

  推荐阅读:

    专辑|Linux文章汇总

    专辑|程序人生

    专辑|C语言

嵌入式Linux

微信扫描二维码,关注我的公众号 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/467244.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【华为出品】物联网全栈开发实战营第2期来啦!送海思开发板

物联网一直在改变我们生活的方方面面。可穿戴设备有助于监控我们的偏好、习惯和健康状况。智能家居设备可提高家居的舒适度、安全性和便利性。城市利用连接的数字设备收集的海量数据(数十亿个)来改善城市规划。制造商使用工业物联网设备来优化工厂车间的操作。据MarketsandMark…

2017年12月计算机一级c,2017年12月计算机二级C语言考试操作题冲刺卷(2)

三. 程序设计题3 [简答题]请编写函数fun&#xff0c;其功能是&#xff1a;移动一维数组中的内容&#xff0c;若数组中有n个整数&#xff0c;要求把下标从0&#xff5e;P(含P&#xff0c;P小于等于n-1)的数组元素平移到数组的最后。例如&#xff0c;一维数组中的原始内容为&…

给楠哥准备的入门单片机

---- 少儿编程前几天&#xff0c;珠海的三哥的老婆微信问我「我想给wending报名这个少儿编程课程&#xff0c;你帮我看看怎么样&#xff1f;」我看了下&#xff0c;除了价格还可以&#xff0c;其他都不怎么样&#xff0c;因为报名了这个课程&#xff0c;大家都知道你们家挺有钱…

应届生昆山offer和上海户口offer要如何选择?

这两天还在苏州&#xff0c;没有感受苏州的什么好地方&#xff0c;天气有点微凉&#xff0c;有秋天的味道&#xff0c;同行的何总是一个很闷的人&#xff0c;我总想让他跟我说说故事&#xff0c;每次他都说「我是一个没有故事的男人」&#xff0c;是的&#xff0c;一个从毕业就…

html5--3.7 input元素(6)

html5--3.7 input元素(6) 学习要点 input元素及其属性input元素 用来设置表单中的内容项&#xff0c;比如输入内容的文本框&#xff0c;按钮等不仅可以布置在表单中&#xff0c;也可以在表单之外的元素使用input元素的属性 type属性&#xff1a;指定输入内容的类型&#xff0c;…

计算机or笔记本,笔记本or台式机?大学生第一个烦恼被它解决了

原标题&#xff1a;笔记本or台式机&#xff1f;大学生第一个烦恼被它解决了台式电脑还是笔记本&#xff1f;许多大学生在选择第一台大学电脑的时候都会面临这个问题。初秋的风带着点点凉意悄然走进了大学校园&#xff0c;开学季来临&#xff0c;又一批莘莘学子即将满怀青春的梦…

把编译时间加入到目标文件

原文&#xff1a;https://www.cnblogs.com/pingwen/p/8183728.html1、问题背景&#xff1a;如何保证发布出去的bin文件是最终测试通过的版本&#xff1f;一般的来讲&#xff0c;代码到了测试后期&#xff0c;master分支就不会频繁的提交了&#xff0c;并且提交也会更加谨慎。但…

大数据基础篇(一):联机分析处理(OLAP) 与 联机事务处理(OLTP)

联机事务处理(OLTP) OLTP也称实时系统(Real Time System)&#xff0c;支持事务快速响应和大并发&#xff0c;这类系统典型的有ATM机(Automated Teller Machine)系统、自动售票系统等&#xff0c;但有些银行转账并不是实时到账的。OLTP反映企业当前的运行状态&#xff0c;完成企…

[JavaScript] 正则表达式

简单模式• 创建&#xff1a; 一个文本格式或正则表达式构造函数 文本格式&#xff1a; /pattern/flags 正则表达式构造函数&#xff1a; new RegExp("pattern"[,"flags"]); • 参数说明&#xff1a; pattern -- 一个正则表达式文本 flags -- 如果存…

1核2G云服务器 标准型S4 S5,腾讯云服务器标准型S4和S5配置性能参数区别及选择攻略...

腾讯云服务器标准型S4和标准型S5实例有什么区别&#xff1f;标准型S5相对于S4是新一代云服务器规格&#xff0c;S5实例CPU采用2.5GHz主频至强Cascade Lake&#xff0c;S4实例处理器采用2.4GHz主频至强Skylake&#xff0c;腾讯云S4/S5均为标准型服务器&#xff0c;具有均衡的计算…

单精度浮点数与十六进制转换

#include <stdio.h>/*--------------------------- 十六进制到浮点数 ---------------------------*/ float Hex_To_Decimal(unsigned char *Byte,int num) { #if 0char cByte[4];//方法一for (int i0;i<num;i){cByte[i] Byte[i];}float pfValue*(float*)&cByte;…

zipline-benchmarks.py文件改写

改写原因&#xff1a;在这个模块中的 get_benchmark_returns() 方法回去谷歌财经下载对应SPY&#xff08;类似于上证指数&#xff09;的数据&#xff0c;但是Google上下载的数据在最后写入Io操作的时候会报一个恶心的编码的错误&#xff0c;很烦人&#xff0c;时好时坏的那种&a…

css普通压缩不去,使用css-loader的minimize压缩css不起作用?

{ test: /\.(scss|css)$/, use: [!env.production? style-loader: MiniCssExtractPlugin.loader,{ loader: css-loader, options: { modules: true, minimize: true //设置css压缩}},{ loader:…

华为3COM交换机PVLAN配置使用说明

1.  PVLAN的引入   <?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" /><?xml:namespace prefix v ns "urn:schemas-microsoft-com:vml" />在实际应用中有这样一个需求&#xff0c;组网图如上图所示。    …

Linux cpuidle framework

背景Kernel版本&#xff1a;4.14ARM64处理器使用工具&#xff1a;Source Insight 3.5&#xff0c; Visio1. 介绍在Linux OS中&#xff0c;Idle进程的运行会让CPU进入cpuidle状态。当没有其他进程处于运行状态时&#xff0c;Scheduler会选择Idle进程来运行&#xff0c;此时CPU无…

Linux利器:QEMU!用它模拟开发板能替代真开发板?

不想错过我的推送&#xff0c;记得右上角-查看公众号-设为星标&#xff0c;摘下星星送给我&#xff01;QEMU&#xff0c;搞嵌入式开发的一定不陌生&#xff0c;最近各大群里都讨论疯了&#xff0c;说它是Linux利器一点也不夸张。它是一款知名的而且开源的模拟器&#xff08;官网…

CPU怎么认识代码的?

# 语言这个东西&#xff1f;首先说明下&#xff0c;我们正常使用的python、C、C语言等等&#xff0c;我们自己能读得懂的语言&#xff0c;包括汇编语言&#xff0c;CPU都是不认识的&#xff0c;CPU 只认识一种语言&#xff0c;那就是 机器语言&#xff0c;也就是我们很多人&…

节日才需要快乐吗?

---- 当然需要天天快乐2020年的国庆和中秋是挺特别的&#xff0c;也是国庆&#xff0c;也是中秋&#xff0c;而且今天还能看NBA总决赛助兴。这么愉快的节日&#xff0c;祝我们的国家繁荣昌盛&#xff0c;国泰民安&#xff0c;也祝大家中秋快乐&#xff0c;当然中秋后也需要快乐…

我在富士康13年

以下是一个读者朋友的日记---- ???? 有点恨铁不成钢的感觉这个读者跟我一样的年纪&#xff0c;都是89年&#xff0c;好吧&#xff0c;说到这里突然觉得又马上要老一岁了&#xff0c;因为小云跟我说&#xff0c;我马上就要过生日了&#xff0c;我从来就记不清自己的生日&…

域名服务器的配置文档,dns域名服务器的配置

dns域名服务器的配置 内容精选换一换使用mount命令挂载文件系统到云服务器&#xff0c;云服务器系统提示timed out。原因1&#xff1a;网络状态不稳定。原因2&#xff1a;网络连接异常。原因3&#xff1a;云服务器DNS配置错误&#xff0c;导致解析不到文件系统的域名&#xff0…