日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android中GC的触发时机和条件

發(fā)布時(shí)間:2023/12/20 Android 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android中GC的触发时机和条件 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

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

Java對(duì)象的創(chuàng)建由Allocator負(fù)責(zé),回收由Collector負(fù)責(zé)。從Android O開始,對(duì)于前臺(tái)應(yīng)用默認(rèn)的GC Collector是CC(Concurrent Copying) Collector,與之相匹配的Allocator則是Region-based Bump Pointer Allocator(with TLAB)。

本文不打算討論CC Collector和Region TLAB的細(xì)節(jié)和實(shí)現(xiàn),一是因?yàn)檫@些內(nèi)容受眾較少,二是因?yàn)槲易约哼€在慢慢摸索和吃透其中的細(xì)節(jié),可能日后會(huì)專門成文介紹他們。

除去繁雜的細(xì)節(jié)介紹,本文希望回答一個(gè)簡單的問題:

對(duì)前臺(tái)應(yīng)用而言,GC到底會(huì)在何時(shí)觸發(fā)?

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?};

根據(jù)GcCause可知,可以觸發(fā)GC的條件還是很多的。對(duì)于開發(fā)者而言,常見的是其中三種:

  • GcCauseForAlloc:通過new分配新對(duì)象時(shí),堆中剩余空間(普通應(yīng)用默認(rèn)上限為256M,聲明largeHeap的應(yīng)用為512M)不足,因此需要先進(jìn)行GC。這種情況會(huì)導(dǎo)致當(dāng)前線程阻塞。

  • GcCauseExplicit:當(dāng)應(yīng)用調(diào)用系統(tǒng)API System.gc()時(shí),會(huì)產(chǎn)生一次GC動(dòng)作。

  • GcCauseBackground:后臺(tái)GC,這里的“后臺(tái)”并不是指應(yīng)用切到后臺(tái)才會(huì)執(zhí)行的GC,而是GC在運(yùn)行時(shí)基本不會(huì)影響其他線程的執(zhí)行,所以也可以理解為并發(fā)GC。相比于前兩種GC,后臺(tái)GC出現(xiàn)的更多也更加隱秘,因此值得詳細(xì)介紹。下文講述的全是這種GC。

Java堆的實(shí)際大小起起伏伏,影響的因素?zé)o非是分配和回收。分配的過程是離散且頻繁的,它來自于不同的工作線程,而且可能每次只分配一小塊區(qū)域。回收的過程則是統(tǒng)一且偶發(fā)的,它由HeapTaskDaemon線程執(zhí)行,在GC的多個(gè)階段中都采用并發(fā)算法,因此不會(huì)暫停工作線程(實(shí)際上會(huì)暫停很短一段時(shí)間)。

當(dāng)我們?cè)贘ava代碼中通過new分配對(duì)象時(shí),虛擬機(jī)會(huì)調(diào)用AllocObjectWithAllocator來執(zhí)行真實(shí)的分配。在每一次成功分配Java對(duì)象后,都會(huì)去檢測是否需要進(jìn)行下一次GC,這就是GcCauseBackground GC的觸發(fā)時(shí)機(jī)。

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?

觸發(fā)的條件需要滿足一個(gè)判斷,如果new_num_bytes_allocated(所有已分配的字節(jié)數(shù),包括此次新分配的對(duì)象) >= concurrent_start_bytes_(下一次GC觸發(fā)的閾值),那么就請(qǐng)求一次新的GC。new_num_bytes_alloated是當(dāng)前分配時(shí)計(jì)算的,concurrent_start_bytes_是上一次GC結(jié)束時(shí)計(jì)算的。以下將分別介紹這兩個(gè)值的計(jì)算過程和背后的設(shè)計(jì)思想。

1. new_num_bytes_allocated的計(jì)算過程

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的實(shí)際分配可以分為三條分支,但如果限定為Region-based Bump Pointer Allocator,則只剩兩條分支:

  • 如果當(dāng)前線程TLAB區(qū)域的剩余空間可以容納下這次分配的對(duì)象,則在TLAB區(qū)域中直接分配。分配算法采用Bump Pointer的方式,僅僅更新已分配區(qū)域的游標(biāo),簡單高效。

    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堆的已使用區(qū)域并沒有增大。這是因?yàn)門LAB在創(chuàng)建之初,它的大小已經(jīng)計(jì)入了num_bytes_allocated_,所以這次雖然分配了新的對(duì)象,但num_bytes_allocated_沒必要增加。

    那么緊接著就來了一個(gè)問題:為什么TLAB在創(chuàng)建之初就要將大小計(jì)入num_bytes_allocated_呢?可是此時(shí)TLAB明明還沒有被使用。這實(shí)際上是一個(gè)空間換時(shí)間的策略。以下是當(dāng)時(shí)這筆改動(dòng)的commit message,通過事先將大小計(jì)入num_bytes_allocated_從而不必要每次分配都更新它,減少針對(duì)num_bytes_allocated_的原子操作,提高性能。代價(jià)就是會(huì)導(dǎo)致num_bytes_allocated_略大于真實(shí)使用的字節(jié)數(shù)。

    [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
  • 如果當(dāng)前線程TLAB區(qū)域的剩余空間無法容納下這次分配的對(duì)象,則為當(dāng)前線程創(chuàng)建一個(gè)新的TLAB。在這種情況下,新分配出來的TLAB大小需要計(jì)入num_bytes_allocated_,因此new_num_bytes_allocated = num_bytes_allocated_before + bytes_tl_bulk_allocated。

  • 2. concurrent_start_bytes_的計(jì)算過程

    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線程執(zhí)行GC時(shí)需要調(diào)用的函數(shù)。其中2671行將執(zhí)行真正的GC,而concurrent_start_bytes_的計(jì)算則在2677行的GrowForUtilization函數(shù)中。

    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_的計(jì)算分為兩個(gè)步驟:

  • 計(jì)算出target_size,一個(gè)僅具有指導(dǎo)意義的最大可分配字節(jié)數(shù)。

  • 根據(jù)target_size計(jì)算出下一次GC的觸發(fā)水位concurrent_start_bytes_。

  • 2.1 target_size的計(jì)算過程

    2.1.1 Sticky GC

    kGcTypeSticky是分代GC下的一種GC類型,表示只針對(duì)兩次GC時(shí)間內(nèi)新分配的對(duì)象進(jìn)行回收,也可以理解為Young-generation GC。如果gc_type為kGcTypeSticky,則執(zhí)行如下過程:

    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與已分配內(nèi)存間可允許的最大差異,差異過小會(huì)導(dǎo)致GC頻繁,差異過大則又會(huì)延遲下一次GC的到來,目前很多設(shè)備將這個(gè)值設(shè)為8M,min_free_設(shè)為512K。其實(shí)針對(duì)RAM超過6G的大內(nèi)存設(shè)備,Google建議可以提高min_free_,用空間換時(shí)間獲取更好的GC性能。multiplier的引入主要是為了優(yōu)化前臺(tái)應(yīng)用,默認(rèn)的前臺(tái)multipiler為2,這樣可以在下次GC前有更多的空間分配對(duì)象。以下是引入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
  • 當(dāng)bytes_allocated + adjusted_max_free < target_footprint時(shí),說明這次GC釋放了很多空間,因此可以適當(dāng)?shù)亟档拖麓蜧C的觸發(fā)水位。

  • 如果bytes_allocated + adjusted_max_free ≥ target_footprint,則取target_footprint和bytes_allocated中的較大值作為target_size。

    這種情況這次GC釋放的空間不多。當(dāng)target_footprint較大時(shí),即便bytes_allocated逼近target_footprint也不增大target_size,是因?yàn)楫?dāng)前GC為Sticky GC(又可理解為Young-generation GC),如果它釋放的空間不多,接下來還可以采用Full GC來更徹底地回收。換言之,只有等Full GC回收完,才能決定將GC的水位提升,因?yàn)檫@時(shí)已經(jīng)嘗試了所有的回收策略。

    當(dāng)bytes_allocated較大時(shí),說明在GC過程中新申請(qǐng)的對(duì)象空間大于GC釋放的空間(因?yàn)椴l(fā),所以申請(qǐng)和釋放可以同步進(jìn)行)。這樣一來,最終計(jì)算的水位值將會(huì)小于bytes_allocated,那豈不是下一次調(diào)用new分配對(duì)象時(shí)必然會(huì)阻塞?實(shí)則不然。因?yàn)椴徽撌莟arget_size還是concurrent_start_bytes_,他們都只有指導(dǎo)意義而無法實(shí)際限制堆內(nèi)存的分配。對(duì)于CC Collector而言,唯一限制堆內(nèi)存分配的只有g(shù)rowth_limit_。不過水位值小于bytes_allocated倒是會(huì)使得下一次對(duì)象分配成功后立馬觸發(fā)一輪新的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???}?

    首先會(huì)根據(jù)目標(biāo)的利用率計(jì)算出新的delta,然后將delta與min_free_和max_free_進(jìn)行比較,使得最終的grow_bytes落在[min_free_,max_free_]之間。target_size的計(jì)算還需考慮multipiler的影響,這樣會(huì)降低前臺(tái)應(yīng)用的堆利用率,從而留有更多空間進(jìn)行分配(降低GC的頻率,代價(jià)就是內(nèi)存資源向前臺(tái)應(yīng)用傾斜)。以下是一部手機(jī)的堆配置,其中數(shù)值可做參考。

    2.2 concurrent_start_bytes_的計(jì)算

    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?????}

    首先需要計(jì)算出在GC過程中新分配的對(duì)象大小,記為bytes_allocated_during_gc。然后將它與kMinConcurrentRemainingBytes和kMaxConcurrentRemainingBytes進(jìn)行比較,使得最終的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_的計(jì)算如下。之所以需要用target_footprint減去remaining_bytes,是因?yàn)樵诶碚撘饬x上,target_footprint_代表當(dāng)前堆的最大可分配字節(jié)數(shù)。而由于是同步GC,回收的過程中可能會(huì)有其他線程依然在分配。所以為了保證本次GC的順利進(jìn)行,需要將這段時(shí)間分配的內(nèi)存空間預(yù)留出來。

    art/runtime/gc/heap.cc

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

    不過需要注意的是,上面闡述的理由僅局限在理論意義上,就像target_footprint_和concurrent_start_bytes_只具有指導(dǎo)意義一樣。所以即便下一次GC過程中分配的內(nèi)存超過了預(yù)留的空間,也并不會(huì)出現(xiàn)內(nèi)存分配不出來而等待的情況。

    ? 推薦閱讀:

    ? ??專輯|Linux文章匯總

    ? ??專輯|程序人生

    ? ??專輯|C語言

    嵌入式Linux

    微信掃描二維碼,關(guān)注我的公眾號(hào)?

    創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

    總結(jié)

    以上是生活随笔為你收集整理的Android中GC的触发时机和条件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。