多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國(guó)內(nèi)最全I(xiàn)T社區(qū)平臺(tái) 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁(yè) > php開(kāi)源 > 綜合技術(shù) > ART運(yùn)行時(shí)為新創(chuàng)建對(duì)象分配內(nèi)存的過(guò)程分析

ART運(yùn)行時(shí)為新創(chuàng)建對(duì)象分配內(nèi)存的過(guò)程分析

來(lái)源:程序員人生   發(fā)布時(shí)間:2015-03-03 08:44:33 閱讀次數(shù):3089次

        ART運(yùn)行時(shí)和Dalvik虛擬機(jī)1樣,在堆上為對(duì)象分配內(nèi)存時(shí)都要解決內(nèi)存碎片和內(nèi)存不足問(wèn)題。內(nèi)存碎片問(wèn)題可使用dlmalloc技術(shù)解決。內(nèi)存不足問(wèn)題則通過(guò)垃圾回收和在允許范圍內(nèi)增長(zhǎng)堆大小解決。由于垃圾回收會(huì)影響程序,因此ART運(yùn)行時(shí)采取力度從小到大的進(jìn)垃圾回收策略。1旦力度小的垃圾回收履行過(guò)后能滿(mǎn)足分配要求,那就不需要進(jìn)行力度大的垃圾回收了。本文就詳細(xì)分析ART運(yùn)行時(shí)在堆上為對(duì)象分配內(nèi)存的進(jìn)程。

本博參加博客之星評(píng)選,求投票:點(diǎn)擊投票

老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關(guān)注!

       從前面ART運(yùn)行時(shí)Java堆創(chuàng)建進(jìn)程分析1文可以知道,在ART運(yùn)行時(shí)中,主要用來(lái)分配對(duì)象的堆空間Zygote Space和Allocation Space的底層使用的都是匿名同享內(nèi)存,并且通過(guò)C庫(kù)提供的malloc和free接口來(lái)分進(jìn)行管理。這樣就能夠通過(guò)dlmalloc技術(shù)來(lái)盡可能解決碎片問(wèn)題。這1點(diǎn)與我們?cè)谇懊鍰alvik虛擬機(jī)為新創(chuàng)建對(duì)象分配內(nèi)存的進(jìn)程分析1文提到的Dalvik虛擬機(jī)解決堆內(nèi)存碎片問(wèn)題的方法是1樣的。因此,接下來(lái)在分析ART運(yùn)行時(shí)為新創(chuàng)建對(duì)象分配的進(jìn)程中,主要會(huì)分析它是如何解決內(nèi)存不足的問(wèn)題的。

       ART運(yùn)行時(shí)為新創(chuàng)建對(duì)象分配的進(jìn)程如圖1所示:

圖1 ART運(yùn)行時(shí)為新創(chuàng)建對(duì)象分配內(nèi)存的進(jìn)程

        對(duì)照Dalvik虛擬機(jī)為新創(chuàng)建對(duì)象分配內(nèi)存的進(jìn)程分析1文的圖2,可以發(fā)現(xiàn),ART運(yùn)行時(shí)和Dalvik虛擬機(jī)為新創(chuàng)建對(duì)象分配內(nèi)存的進(jìn)程幾近是1模1樣的,它們的區(qū)分僅僅是在于垃圾搜集的方式和策略不同。

        從前面Android運(yùn)行時(shí)ART履行類(lèi)方法的進(jìn)程分析1文可以知道,ART運(yùn)行時(shí)為從DEX字節(jié)碼翻譯得到的Native代碼提供的1個(gè)函數(shù)調(diào)用表中,有1個(gè)pAllocObject接口,是用來(lái)分配對(duì)象的。當(dāng)ART運(yùn)行時(shí)以Quick模式運(yùn)行在ARM體系結(jié)構(gòu)時(shí),上述提到的pAllocObject接口由函數(shù)art_quick_alloc_object來(lái)實(shí)現(xiàn)。因此,接下來(lái)我們就從函數(shù)art_quick_alloc_object的實(shí)現(xiàn)開(kāi)始分析ART運(yùn)行時(shí)為新創(chuàng)建對(duì)象分配內(nèi)存的進(jìn)程。

        函數(shù)art_quick_alloc_object的實(shí)現(xiàn)以下所示:

/* * Called by managed code to allocate an object */ .extern artAllocObjectFromCode ENTRY art_quick_alloc_object SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC mov r2, r9 @ pass Thread::Current mov r3, sp @ pass SP bl artAllocObjectFromCode @ (uint32_t type_idx, Method* method, Thread*, SP) RESTORE_REF_ONLY_CALLEE_SAVE_FRAME RETURN_IF_RESULT_IS_NON_ZERO DELIVER_PENDING_EXCEPTION END art_quick_alloc_object
       這個(gè)函數(shù)定義在文件art/runtime/arch/arm/quick_entrypoints_arm.S中。

       這是1段ARM匯編,我們需要注意的1點(diǎn)是Native代碼調(diào)用ART運(yùn)行時(shí)提供的對(duì)象分配接口的參數(shù)傳遞方式。其中,參數(shù)type_idx描寫(xiě)的是要分配的對(duì)象的類(lèi)型,通過(guò)寄存器r0傳遞,參數(shù)method描寫(xiě)的是當(dāng)前調(diào)用的類(lèi)方法,通過(guò)寄存器r1傳遞。

       函數(shù)art_quick_alloc_object是通過(guò)調(diào)用另外1個(gè)函數(shù)artAllocObjectFromCode來(lái)分配對(duì)象的。函數(shù)art_quick_alloc_object除傳遞前面描寫(xiě)的參數(shù)type_idx和method給函數(shù)artAllocObjectFromCode以外,還會(huì)傳遞另外的兩個(gè)參數(shù)。其中1個(gè)是描寫(xiě)當(dāng)前線(xiàn)程的1個(gè)Thread對(duì)象,該對(duì)象總是保存在寄存器r9中,現(xiàn)在由于要通過(guò)參數(shù)的情勢(shì)傳遞給另外1個(gè)函數(shù),因此就將它放在寄存器r2。另外1個(gè)是棧指針sp,也是由于要通過(guò)參數(shù)的情勢(shì)的傳遞另外1個(gè)函數(shù),這里也會(huì)將它放在寄存器r3中。

       函數(shù)artAllocObjectFromCode的實(shí)現(xiàn)以下所示:

extern "C" mirror::Object* artAllocObjectFromCode(uint32_t type_idx, mirror::ArtMethod* method, Thread* self, mirror::ArtMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); return AllocObjectFromCode(type_idx, method, self, false); }
        這個(gè)函數(shù)定義在文件art/runtime/entrypoints/quick/quick_alloc_entrypoints.cc中。

        函數(shù)artAllocObjectFromCode又是通過(guò)調(diào)用另外1個(gè)函數(shù)AllocObjectFromCode來(lái)分配對(duì)象的。不過(guò),在調(diào)用函數(shù)AllocObjectFromCode之前,函數(shù)artAllocObjectFromCode會(huì)先調(diào)用另外1個(gè)函數(shù)FinishCalleeSaveFrameSetup在當(dāng)前調(diào)用棧幀中保存1個(gè)運(yùn)行時(shí)信息。這個(gè)運(yùn)行時(shí)信息描寫(xiě)的是接下來(lái)要調(diào)用的方法的類(lèi)型為Runtime::kRefsOnly,也就是由被調(diào)用者保存那些不是用來(lái)傳遞參數(shù)的通用寄存器,即除r0-r3的其它通用寄存器。

        函數(shù)AllocObjectFromCode的實(shí)現(xiàn)以下所示:

// Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it // cannot be resolved, throw an error. If it can, use it to create an instance. // When verification/compiler hasn't been able to verify access, optionally perform an access // check. static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx, mirror::ArtMethod* method, Thread* self, bool access_check) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx); Runtime* runtime = Runtime::Current(); if (UNLIKELY(klass == NULL)) { klass = runtime->GetClassLinker()->ResolveType(type_idx, method); if (klass == NULL) { DCHECK(self->IsExceptionPending()); return NULL; // Failure } } if (access_check) { if (UNLIKELY(!klass->IsInstantiable())) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); self->ThrowNewException(throw_location, "Ljava/lang/InstantiationError;", PrettyDescriptor(klass).c_str()); return NULL; // Failure } mirror::Class* referrer = method->GetDeclaringClass(); if (UNLIKELY(!referrer->CanAccess(klass))) { ThrowIllegalAccessErrorClass(referrer, klass); return NULL; // Failure } } if (!klass->IsInitialized() && !runtime->GetClassLinker()->EnsureInitialized(klass, true, true)) { DCHECK(self->IsExceptionPending()); return NULL; // Failure } return klass->AllocObject(self); }
       這個(gè)函數(shù)定義在文件art/runtime/entrypoints/entrypoint_utils.h中。

       參數(shù)type_idx描寫(xiě)的是要分配的對(duì)象的類(lèi)型,函數(shù)AllocObjectFromCode需要將它解析為1個(gè)Class對(duì)象,以即可以取得更多的信息進(jìn)行內(nèi)存分配。

       函數(shù)AllocObjectFromCode首先是在當(dāng)前調(diào)用類(lèi)方法method的Dex Cache中檢查是不是已存在1個(gè)與參數(shù)type_idx對(duì)應(yīng)的Class對(duì)象。如果已存在,那末就說(shuō)明參數(shù)type_idx描寫(xiě)的對(duì)象類(lèi)型已被加載和解析過(guò)了,因此這時(shí)候候就能夠直接拿來(lái)使用。否則的話(huà),就通過(guò)調(diào)用保存在當(dāng)前運(yùn)行時(shí)對(duì)象內(nèi)部的1個(gè)ClassLinker對(duì)象的成員函數(shù)ResolveType來(lái)對(duì)參數(shù)type_idx描寫(xiě)的對(duì)象類(lèi)型進(jìn)行加載和解析。關(guān)于Dex Cache的知識(shí),可以參數(shù)前面Android運(yùn)行時(shí)ART履行類(lèi)方法的進(jìn)程分析1文,而對(duì)象類(lèi)型(即類(lèi))的加載和解析進(jìn)程可以參考前面Android運(yùn)行時(shí)ART加載類(lèi)和方法的進(jìn)程分析1文。

       得到了要分配的對(duì)象的類(lèi)型klass以后,如果參數(shù)access_check的值等于true,那末就對(duì)該類(lèi)型進(jìn)行檢查,即檢查它是不是可以實(shí)例化和是不是可以訪(fǎng)問(wèn)。如果檢查通過(guò),或不需要檢查,那末接下來(lái)還要確保類(lèi)型klass是已初始化過(guò)了的。前面的檢查都沒(méi)有問(wèn)題以后,最后函數(shù)AllocObjectFromCode就調(diào)用Class類(lèi)的成員函數(shù)AllocObject來(lái)分配1個(gè)類(lèi)型為klass的對(duì)象。

       Class類(lèi)的成員函數(shù)AllocObject的實(shí)現(xiàn)以下所示:

Object* Class::AllocObject(Thread* self) { ...... return Runtime::Current()->GetHeap()->AllocObject(self, this, this->object_size_); }
      這個(gè)函數(shù)定義在文件art/runtime/mirror/class.cc中。

      這里我們就終究看到調(diào)用ART運(yùn)行時(shí)內(nèi)部的Heap對(duì)象的成員函數(shù)AllocObject在堆上分配對(duì)象了,其中,要分配的大小保存在當(dāng)前Class對(duì)象的成員變量object_size_中。

      Heap類(lèi)的成員函數(shù)AllocObject的實(shí)現(xiàn)以下所示:

mirror::Object* Heap::AllocObject(Thread* self, mirror::Class* c, size_t byte_count) { ...... mirror::Object* obj = NULL; size_t bytes_allocated = 0; ...... 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); ...... } if (LIKELY(obj != NULL)) { obj->SetClass(c); ...... RecordAllocation(bytes_allocated, obj); ...... if (UNLIKELY(static_cast<size_t>(num_bytes_allocated_) >= concurrent_start_bytes_)) { ...... SirtRef<mirror::Object> ref(self, obj); RequestConcurrentGC(self); } ...... return obj; } else { ...... self->ThrowOutOfMemoryError(oss.str().c_str()); return NULL; } }

        這個(gè)函數(shù)定義在文件art/runtime/gc/heap.cc中。

        Heap類(lèi)的成員函數(shù)AllocObject首先是要肯定要在哪一個(gè)Space上分配內(nèi)存??梢苑峙鋬?nèi)存的Space有3個(gè),分別Zygote Space、Allocation Space和Large Space。不過(guò),Zygote Space在還沒(méi)有劃分出Allocation Space之前,就在Zygote Space上分配,而當(dāng)Zygote Space劃分出Allocation Space以后,就只能在Allocation Space上分配。從前面ART運(yùn)行時(shí)Java堆創(chuàng)建進(jìn)程分析1文可以知道,Heap類(lèi)的成員變量alloc_space_在Zygote Space在還沒(méi)有劃分出Allocation Space之前指向Zygote Space,劃分以后就指向Allocation Space。Large Object Space則始終由Heap類(lèi)的成員變量large_object_space_指向。

        只要滿(mǎn)足以下3個(gè)條件,就在Large Object Space上分配,否則就在Zygote Space或Allocation Space上分配:

        1. 要求分配的內(nèi)存大于等于Heap類(lèi)的成員變量large_object_threshold_指定的值。這個(gè)值等于3 * kPageSize,即3個(gè)頁(yè)面的大小。

        2. 已從Zygote Space劃分出Allocation Space,即Heap類(lèi)的成員變量have_zygote_space_的值等于true。

        3. 被分配的對(duì)象是1個(gè)原子類(lèi)型數(shù)組,即byte數(shù)組、int數(shù)組和boolean數(shù)組等。

        肯定好要在哪一個(gè)Space上分配內(nèi)存以后,就能夠調(diào)用Heap類(lèi)的成員函數(shù)Allocate進(jìn)行分配了。如果分配成功,Heap類(lèi)的成員函數(shù)Allocate就返回新分配的對(duì)象,保存在變量obj中。接下來(lái)再做3件事情:

        1. 調(diào)用Object類(lèi)的成員函數(shù)SetClass設(shè)置新分配對(duì)象obj的類(lèi)型。

        2. 調(diào)用Heap類(lèi)的成員函數(shù)RecordAllocation記錄當(dāng)前的內(nèi)存分配狀態(tài)。

        3. 檢查當(dāng)前已分配出去的內(nèi)存是不是已到達(dá)由Heap類(lèi)的成員變量concurrent_start_bytes_設(shè)定的閥值。如果到達(dá),那末就調(diào)用Heap類(lèi)的成員函數(shù)RequestConcurrentGC通知GC履行1次并行GC。關(guān)于履行并行GC的閥值,接下來(lái)分要ART運(yùn)行時(shí)的垃圾搜集進(jìn)程中再詳細(xì)分析。

        另外一方面,如果Heap類(lèi)的成員函數(shù)Allocate分配內(nèi)存失敗,則Heap類(lèi)的成員函數(shù)AllocObject拋出1個(gè)OOM異常。

        接下來(lái),我們先分析Heap類(lèi)的成員函數(shù)RecordAllocation的實(shí)現(xiàn),接著再分析Heap類(lèi)的成員函數(shù)Allocate的實(shí)現(xiàn)。由于后者的履行流程比較復(fù)雜,而前者的履行流程比較簡(jiǎn)單。我們先分析容易的,以避免打斷后面的分析。

        Heap類(lèi)的成員函數(shù)RecordAllocation的實(shí)現(xiàn)以下所示:

inline void Heap::RecordAllocation(size_t size, mirror::Object* obj) { DCHECK(obj != NULL); DCHECK_GT(size, 0u); num_bytes_allocated_.fetch_add(size); if (Runtime::Current()->HasStatsEnabled()) { RuntimeStats* thread_stats = Thread::Current()->GetStats(); ++thread_stats->allocated_objects; thread_stats->allocated_bytes += size; // TODO: Update these atomically. RuntimeStats* global_stats = Runtime::Current()->GetStats(); ++global_stats->allocated_objects; global_stats->allocated_bytes += size; } // This is safe to do since the GC will never free objects which are neither in the allocation // stack or the live bitmap. while (!allocation_stack_->AtomicPushBack(obj)) { CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false); } }
        這個(gè)函數(shù)定義在文件art/runtime/gc/heap.cc中。

        Heap類(lèi)的成員函數(shù)RecordAllocation首先是記錄當(dāng)前已分配的內(nèi)存字節(jié)數(shù)和對(duì)象數(shù),接著再將新分配的對(duì)角壓入到Heap類(lèi)的成員變量allocation_stack_描寫(xiě)的Allocation Stack中去。后面這1點(diǎn)與Dalvik虛擬機(jī)的做法是不1樣的。Dalvik虛擬機(jī)直接將新分配出來(lái)的對(duì)象記錄在Live Bitmap中,具體可以參考前面Dalvik虛擬機(jī)為新創(chuàng)建對(duì)象分配內(nèi)存的進(jìn)程分析1文。ART運(yùn)行時(shí)之所以要將新分配的對(duì)象壓入到Allocation Stack中去,是為了以后可以履行Sticky GC。

        注意,如果不能成功將將新分配的對(duì)角壓入到Allocation Stack中,就說(shuō)明上次GC以來(lái),新分配的對(duì)象太多了,因此這時(shí)候候就需要履行1個(gè)Sticky GC,將Allocation Stack里面的垃圾進(jìn)行回收,然后再?lài)L試將新分配的對(duì)象壓入到Allocation Stack中,直到成功為止。

        接下來(lái)我們就重點(diǎn)分析Heap類(lèi)的成員函數(shù)Allocate的實(shí)現(xiàn),以即可以了解新創(chuàng)建對(duì)象在堆上分配的具體進(jìn)程,以下所示:

template <class T> inline mirror::Object* Heap::Allocate(Thread* self, T* space, size_t alloc_size, size_t* bytes_allocated) { ...... mirror::Object* ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated); if (ptr != NULL) { return ptr; } return AllocateInternalWithGc(self, space, alloc_size, bytes_allocated); }

        這個(gè)函數(shù)定義在文件art/runtime/gc/heap.cc中。

        Heap類(lèi)的成員函數(shù)Allocate首先調(diào)用成員函數(shù)TryToAllocate嘗試在不履行GC的情況下進(jìn)行內(nèi)存分配。如果分配失敗,再調(diào)用成員函數(shù)AllocateInternalWithGc進(jìn)行帶GC的內(nèi)存分配。

       Heap類(lèi)的成員函數(shù)Allocate是1個(gè)模板函數(shù),不同類(lèi)型的Space會(huì)致使調(diào)用不同重載的成員函數(shù)TryToAllocate進(jìn)行不帶GC的內(nèi)存分配。雖然可以用來(lái)分配內(nèi)存的Space有Zygote Space、Allocation Space和Large Object Space3個(gè),但是前二者的類(lèi)型是相同的,因此實(shí)際上只有兩個(gè)不同重載版本的成員函數(shù)TryToAllocate,它們的實(shí)現(xiàn)以下所示:

inline mirror::Object* Heap::TryToAllocate(Thread* self, space::AllocSpace* space, size_t alloc_size, bool grow, size_t* bytes_allocated) { if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { return NULL; } return space->Alloc(self, alloc_size, bytes_allocated); } // DlMallocSpace-specific version. inline mirror::Object* Heap::TryToAllocate(Thread* self, space::DlMallocSpace* space, size_t alloc_size, bool grow, size_t* bytes_allocated) { if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { return NULL; } if (LIKELY(!running_on_valgrind_)) { return space->AllocNonvirtual(self, alloc_size, bytes_allocated); } else { return space->Alloc(self, alloc_size, bytes_allocated); } }
        這兩個(gè)函數(shù)定義在文件art/runtime/gc/heap.cc中。

        Heap類(lèi)兩個(gè)重載版本的成員函數(shù)TryToAllocate的實(shí)現(xiàn)邏輯都幾近是相同的,首先是調(diào)用另外1個(gè)成員函數(shù)IsOutOfMemoryOnAllocation判斷分配要求的內(nèi)存后是不是會(huì)超過(guò)堆的大小限制。如果超過(guò),則分配失??;否則的話(huà)再在指定的Space進(jìn)行內(nèi)存分配。

        Heap類(lèi)的成員函數(shù)IsOutOfMemoryOnAllocation的實(shí)現(xiàn)以下所示:

inline bool Heap::IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow) { size_t new_footprint = num_bytes_allocated_ + alloc_size; if (UNLIKELY(new_footprint > max_allowed_footprint_)) { if (UNLIKELY(new_footprint > growth_limit_)) { return true; } if (!concurrent_gc_) { if (!grow) { return true; } else { max_allowed_footprint_ = new_footprint; } } } return false; }
        這個(gè)函數(shù)定義在文件art/runtime/gc/heap.cc中。

        Heap類(lèi)的成員變量num_bytes_allocated_描寫(xiě)的是目前已分配出去的內(nèi)存字節(jié)數(shù),成員變量max_allowed_footprint_描寫(xiě)的是目前堆可分配的最大內(nèi)存字節(jié)數(shù),成員變量growth_limit_描寫(xiě)的是目前堆允許增長(zhǎng)到的最大內(nèi)存字節(jié)數(shù)。這里需要注意的1點(diǎn)是,max_allowed_footprint_是Heap類(lèi)施加的1個(gè)限制,不會(huì)對(duì)各個(gè)Space實(shí)際可分配的最大內(nèi)存字節(jié)數(shù)產(chǎn)生影響,并且各個(gè)Space在創(chuàng)建的時(shí)候,已把自己可分配的最大內(nèi)存數(shù)設(shè)置為允許使用的最大內(nèi)存字節(jié)數(shù)的。

        如果目前堆已分配出去的內(nèi)存字節(jié)數(shù)再加上要求分配的內(nèi)存字節(jié)數(shù)new_footprint小于等于目前堆可分配的最大內(nèi)存字節(jié)數(shù)max_allowed_footprint_,那末分配出要求的內(nèi)存字節(jié)數(shù)以后不會(huì)造成OOM,因此Heap類(lèi)的成員函數(shù)IsOutOfMemoryOnAllocation就返回false。。

        另外一方面,如果目前堆已分配出去的內(nèi)存字節(jié)數(shù)再加上要求分配的內(nèi)存字節(jié)數(shù)new_footprint大于目前堆可分配的最大內(nèi)存字節(jié)數(shù)max_allowed_footprint_,并且也大于目前堆允許增長(zhǎng)到的最大內(nèi)存字節(jié)數(shù)growth_limit_,那末分配出要求的內(nèi)存字節(jié)數(shù)以后造成OOM,因此Heap類(lèi)的成員函數(shù)IsOutOfMemoryOnAllocation就返回true。

       剩下另外1種情況,目前堆已分配出去的內(nèi)存字節(jié)數(shù)再加上要求分配的內(nèi)存字節(jié)數(shù)new_footprint大于目前堆可分配的最大內(nèi)存字節(jié)數(shù)max_allowed_footprint_,但是小于等于目前堆允許增長(zhǎng)到的最大內(nèi)存字節(jié)數(shù)growth_limit_,這時(shí)候候就要看情況了會(huì)不會(huì)出現(xiàn)OOM了。如果ART運(yùn)行時(shí)運(yùn)行在非并行GC的模式中,即Heap類(lèi)的成員變量concurrent_gc_等于false,那末取決于允不允許增長(zhǎng)堆的大小,即參數(shù)grow的值。如果不允許,那末Heap類(lèi)的成員函數(shù)IsOutOfMemoryOnAllocation就返回true,表示當(dāng)前要求的分配會(huì)造成OOM。如果允許,那末Heap類(lèi)的成員函數(shù)IsOutOfMemoryOnAllocation就會(huì)修改目前堆可分配的最大內(nèi)存字節(jié)數(shù)max_allowed_footprint_,并且返回false,表示允許當(dāng)前要求的分配。這意味著,在非并行GC運(yùn)行模式中,在分配內(nèi)存進(jìn)程中遇到內(nèi)存不足,并且當(dāng)前可分配內(nèi)存還未到達(dá)增長(zhǎng)上限時(shí),要等到履行完成1次非并行GC后,才能成功分配到內(nèi)存,由于每次履行完成GC以后,都會(huì)依照預(yù)先設(shè)置的堆目標(biāo)利用率來(lái)增長(zhǎng)堆的大小。

        另外一方面,如果ART運(yùn)行時(shí)運(yùn)行在并行GC的模式中,那末只要前堆已分配出去的內(nèi)存字節(jié)數(shù)再加上要求分配的內(nèi)存字節(jié)數(shù)new_footprint不地超過(guò)目前堆允許增長(zhǎng)到的最大內(nèi)存字節(jié)數(shù)growth_limit_,那末就不管允不允許增長(zhǎng)堆的大小,都認(rèn)為不會(huì)產(chǎn)生OOM,因此Heap類(lèi)的成員函數(shù)IsOutOfMemoryOnAllocation就返回false。這意味著,在并行GC運(yùn)行模式中,在分配內(nèi)存進(jìn)程中遇到內(nèi)存不足,并且當(dāng)前可分配內(nèi)存還未到達(dá)增長(zhǎng)上限時(shí),不過(guò)等到履行并行GC后,就有可能成功分配到內(nèi)存,由于實(shí)際履行內(nèi)存分配的Space可分配的最大內(nèi)存字節(jié)數(shù)是足夠的。

        回到前面Heap類(lèi)的成員函數(shù)TryToAllocate中,從前面ART運(yùn)行時(shí)Java堆創(chuàng)建進(jìn)程分析1文可以知道,對(duì)Large Object Space版本的成員函數(shù)TryToAllocate,調(diào)用的是LargeObjectMapSpace類(lèi)的成員函數(shù)Alloc進(jìn)行內(nèi)存分配,而對(duì)Zygote Space或Allocation Space版本的成員函數(shù)TryToAllocate,如果成員變量running_on_valgrind_的值等于true,就調(diào)用ValgrindDlMallocSpace類(lèi)的成員函數(shù)AllocNonvirtual進(jìn)行內(nèi)存分配,否則就調(diào)用DlMallocSpace類(lèi)的成員函數(shù)Alloc進(jìn)行內(nèi)存分配。我們假定Heap類(lèi)的成員變量running_on_valgrind_的值等于false,因此接下來(lái)我們主要分析LargeObjectMapSpace類(lèi)的成員函數(shù)Alloc和DlMallocSpace類(lèi)的成員函數(shù)Alloc的實(shí)現(xiàn)。

       LargeObjectMapSpace類(lèi)的成員函數(shù)Alloc的實(shí)現(xiàn)以下所示:

mirror::Object* LargeObjectMapSpace::Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated) { MemMap* mem_map = MemMap::MapAnonymous("large object space allocation", NULL, num_bytes, PROT_READ | PROT_WRITE); if (mem_map == NULL) { return NULL; } MutexLock mu(self, lock_); mirror::Object* obj = reinterpret_cast<mirror::Object*>(mem_map->Begin()); large_objects_.push_back(obj); mem_maps_.Put(obj, mem_map); size_t allocation_size = mem_map->Size(); DCHECK(bytes_allocated != NULL); *bytes_allocated = allocation_size; num_bytes_allocated_ += allocation_size; total_bytes_allocated_ += allocation_size; ++num_objects_allocated_; ++total_objects_allocated_; return obj; }
       這個(gè)函數(shù)定義在文件art/runtime/gc/space/large_object_space.cc中。

       從這里就能夠看到,Large Object Map Space分配內(nèi)存的邏輯是很簡(jiǎn)單的,直接就是調(diào)用MemMap類(lèi)的靜態(tài)成員函數(shù)MapAnonymous創(chuàng)建1塊指定大小的匿名內(nèi)存,然后再將該匿名同享內(nèi)存添加到成員變量large_objects_描寫(xiě)的1個(gè)向量中去,最后更新內(nèi)部的各個(gè)統(tǒng)計(jì)數(shù)據(jù)。

       DlMallocSpace類(lèi)的成員函數(shù)Alloc的實(shí)現(xiàn)以下所示:

mirror::Object* DlMallocSpace::Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated) { return AllocNonvirtual(self, num_bytes, bytes_allocated); }
       這個(gè)函數(shù)定義在文件art/runtime/gc/space/dlmalloc_space.cc中。

       DlMallocSpace類(lèi)的成員函數(shù)Alloc調(diào)用另外1個(gè)成員函數(shù)AllocNonvirtual來(lái)進(jìn)行內(nèi)存分配,后者的實(shí)現(xiàn)以下所示:

inline mirror::Object* DlMallocSpace::AllocNonvirtual(Thread* self, size_t num_bytes, size_t* bytes_allocated) { mirror::Object* obj; { MutexLock mu(self, lock_); obj = AllocWithoutGrowthLocked(num_bytes, bytes_allocated); } if (obj != NULL) { // Zero freshly allocated memory, done while not holding the space's lock. memset(obj, 0, num_bytes); } return obj; }
       這個(gè)函數(shù)定義在文件art/runtime/gc/space/dlmalloc_space-inl.h中。

       DlMallocSpace類(lèi)的成員函數(shù)AllocNonvirtual首先是調(diào)用另外1個(gè)成員函數(shù)AllocWithoutGrowthLocked在不增長(zhǎng)Space的大小的條件下進(jìn)行內(nèi)存分配,分配成功以后再調(diào)用函數(shù)memset對(duì)分配出來(lái)的內(nèi)存進(jìn)行清空,最后將分配出來(lái)的內(nèi)存返回給調(diào)用者。

       DlMallocSpace類(lèi)的成員函數(shù)AllocWithoutGrowthLocked的實(shí)現(xiàn)以下所示:

inline mirror::Object* DlMallocSpace::AllocWithoutGrowthLocked(size_t num_bytes, size_t* bytes_allocated) { mirror::Object* result = reinterpret_cast<mirror::Object*>(mspace_malloc(mspace_, num_bytes)); if (result != NULL) { if (kDebugSpaces) { CHECK(Contains(result)) << "Allocation (" << reinterpret_cast<void*>(result) << ") not in bounds of allocation space " << *this; } size_t allocation_size = AllocationSizeNonvirtual(result); DCHECK(bytes_allocated != NULL); *bytes_allocated = allocation_size; num_bytes_allocated_ += allocation_size; total_bytes_allocated_ += allocation_size; ++total_objects_allocated_; ++num_objects_allocated_; } return result; }
        這個(gè)函數(shù)定義在文件art/runtime/gc/space/dlmalloc_space-inl.h中。

        從前面ART運(yùn)行時(shí)Java堆創(chuàng)建進(jìn)程分析1文可以知道,DlMallocSpace底層使用的匿名同享內(nèi)存塊被封裝成1個(gè)mspace對(duì)象,并且保存在成員變量mspace_中,因此這里就能夠直接調(diào)用C庫(kù)提供的mspace_malloc接口進(jìn)行內(nèi)存分配。使用mspace_malloc分配的內(nèi)存會(huì)自動(dòng)被清空,因此這里不用再手動(dòng)清空。DlMallocSpace類(lèi)的成員函數(shù)AllocWithoutGrowthLocked在將分配出來(lái)的內(nèi)存返回給調(diào)用者之前,一樣是會(huì)更新內(nèi)部的各個(gè)統(tǒng)計(jì)數(shù)據(jù)。

        回到前面Heap類(lèi)的成員函數(shù)Allocate中,在調(diào)用成員函數(shù)TryToAllocate不能成功分配指定大小的內(nèi)存塊以后,接下來(lái)就繼續(xù)調(diào)用成員函數(shù)AllocateInternalWithGc履行帶GC的內(nèi)存分配,它的實(shí)現(xiàn)以下所示:

mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* space, size_t alloc_size, size_t* bytes_allocated) { mirror::Object* ptr; // The allocation failed. If the GC is running, block until it completes, and then retry the // allocation. collector::GcType last_gc = WaitForConcurrentGcToComplete(self); if (last_gc != collector::kGcTypeNone) { // A GC was in progress and we blocked, retry allocation now that memory has been freed. ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated); if (ptr != NULL) { return ptr; } } // Loop through our different Gc types and try to Gc until we get enough free memory. for (size_t i = static_cast<size_t>(last_gc) + 1; i < static_cast<size_t>(collector::kGcTypeMax); ++i) { bool run_gc = false; collector::GcType gc_type = static_cast<collector::GcType>(i); switch (gc_type) { case collector::kGcTypeSticky: { const size_t alloc_space_size = alloc_space_->Size(); run_gc = alloc_space_size > min_alloc_space_size_for_sticky_gc_ && alloc_space_->Capacity() - alloc_space_size >= min_remaining_space_for_sticky_gc_; break; } case collector::kGcTypePartial: run_gc = have_zygote_space_; break; case collector::kGcTypeFull: run_gc = true; break; default: break; } if (run_gc) { // If we actually ran a different type of Gc than requested, we can skip the index forwards. collector::GcType gc_type_ran = CollectGarbageInternal(gc_type, kGcCauseForAlloc, false); DCHECK_GE(static_cast<size_t>(gc_type_ran), i); i = static_cast<size_t>(gc_type_ran); // Did we free sufficient memory for the allocation to succeed? ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated); if (ptr != NULL) { return ptr; } } } // Allocations have failed after GCs; this is an exceptional state. // Try harder, growing the heap if necessary. ptr = TryToAllocate(self, space, alloc_size, true, bytes_allocated); if (ptr != NULL) { return ptr; } ...... // We don't need a WaitForConcurrentGcToComplete here either. CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true); return TryToAllocate(self, space, alloc_size, true, bytes_allocated); }
        這個(gè)函數(shù)定義在文件art/runtime/gc/heap.cc中。

        Heap類(lèi)的成員函數(shù)AllocateInternalWithGc主要是通過(guò)垃圾回收來(lái)滿(mǎn)足要求分配的內(nèi)存,它的履行邏輯以下所示:

        1. 調(diào)用Heap類(lèi)的成員函數(shù)WaitForConcurrentGcToComplete檢查是不是有并行GC正在履行。如果有的話(huà),就等待其履行完成,并且得到它的類(lèi)型last_gc。如果last_gc如果不等于collector::kGcTypeNone,就表示有并行GC并且已履行完成,因此就能夠調(diào)用Heap類(lèi)的成員函數(shù)TryToAllocate在不增長(zhǎng)當(dāng)前堆大小的條件下再次嘗試分配要求的內(nèi)存了。如果分配成功,則返回得到的內(nèi)存起始地址給調(diào)用者。否則的話(huà),繼續(xù)往下履行。

        2.  順次履行kGcTypeSticky、kGcTypePartial和kGcTypeFull3種類(lèi)型的GC。每次GC履行終了,都嘗試調(diào)用Heap類(lèi)的成員函數(shù)TryToAllocate在不增長(zhǎng)當(dāng)前堆大小的條件下再次嘗試分配要求的內(nèi)存。如果分配內(nèi)存成功,則返回得到的內(nèi)存起始地址給調(diào)用者,并且不再履行下1個(gè)種類(lèi)型的GC。

        這里需要注意的1點(diǎn)是,kGcTypeSticky、kGcTypePartial和kGcTypeFull3種類(lèi)型的GC的垃圾回收力度是順次加強(qiáng):kGcTypeSticky只回收上次GC后在Allocation Space中新分配的垃圾對(duì)象;kGcTypePartial只回收Allocation Space的垃圾對(duì)象;kGcTypeFull同時(shí)回收Z(yǔ)ygote Space和Allocation Space的垃圾對(duì)象。通過(guò)這類(lèi)策略,就有可能以最小代價(jià)解決分配對(duì)象時(shí)遇到的內(nèi)在不足問(wèn)題。不過(guò),對(duì)類(lèi)型為kGcTypeSticky和kGcTypePartial的GC,它們的履行是條件的

        類(lèi)型為kGcTypeSticky的GC的履行代碼雖然是最小的,但是它能夠回收的垃圾也是最小的。如果回收的垃圾不足于滿(mǎn)足要求分配的內(nèi)存,那就相當(dāng)于做了1次無(wú)用功了。因此,履行類(lèi)型為kGcTypeSticky的GC需要滿(mǎn)足兩個(gè)條件。第1個(gè)條件是上次GC后在Allocation Space上分配的內(nèi)存要到達(dá)1定的閥值,這樣才有比較大的幾率回收到較多的內(nèi)存。第2個(gè)條件Allocation Space剩余的未分配內(nèi)存要到達(dá)1定的閥值,這樣可以保證在回收得到較少內(nèi)存時(shí),也有比較大的幾率滿(mǎn)足要求分配的內(nèi)存。前1個(gè)閥值定義在Heap類(lèi)的成員變量min_alloc_space_size_for_sticky_gc_中,它的值設(shè)置為2M,而上次GC以來(lái)分配的內(nèi)存通過(guò)當(dāng)前Allocation Space的大小估算得到,即通過(guò)調(diào)用Heap類(lèi)的成員變量alloc_space_指向的1個(gè)DlMallocSpace對(duì)象的成員函數(shù)Size取得。后1個(gè)閥值定義在Heap類(lèi)的成員變量min_remaining_space_for_sticky_gc_中,它的值設(shè)置為1M,而Allocation Space剩余的未分配內(nèi)存可以用Allocation Space的總大小減去當(dāng)前Allocation Space的大小得到。通過(guò)調(diào)用Heap類(lèi)的成員變量alloc_space_指向的1個(gè)DlMallocSpace對(duì)象的成員函數(shù)Capacity取得其總大小。

        類(lèi)型為kGcTypePartial的GC的履行條件是已從Zygote Space中劃分出Allocation Space。從前面ART運(yùn)行時(shí)Java堆創(chuàng)建進(jìn)程分析1文可以知道,當(dāng)Heap類(lèi)的成員變量have_zygote_space_的值等于true時(shí),就表明已從Zygote Space中劃分出Allocation Space了。因此,在這類(lèi)情況下,就能夠履行類(lèi)型為kGcTypePartial的GC了。

        每種類(lèi)型的GC都是通過(guò)調(diào)用Heap類(lèi)的成員函數(shù)CollectGarbageInternal來(lái)履行。注意這時(shí)候候調(diào)用Heap類(lèi)的成員函數(shù)CollectGarbageInternal傳遞的第3個(gè)參數(shù)為false,表示不對(duì)那些只被軟援用對(duì)象援用的對(duì)象進(jìn)行回收。如果上述的3種類(lèi)型的GC履行終了,還是不能滿(mǎn)足分配要求的內(nèi)存,則繼續(xù)往下履行。

        3. 經(jīng)過(guò)前面3種類(lèi)型的GC后還是不能成功分配到內(nèi)存,那就說(shuō)明能夠回收的內(nèi)存還是太小了,因此,這時(shí)候候只能通過(guò)在允許范圍內(nèi)增長(zhǎng)堆的大小來(lái)滿(mǎn)足內(nèi)存分配要求了。前面分析Heap類(lèi)的成員函數(shù)TryToAlloctate時(shí),將第4個(gè)參數(shù)設(shè)置為true,便可在允許范圍內(nèi)增長(zhǎng)堆大小的條件下進(jìn)行內(nèi)存分配。如果在允許范圍內(nèi)增長(zhǎng)了堆的大小還是不能成功分配到要求的內(nèi)存,那就只能出最后的1個(gè)大招了。

        4.  最后的大招是首先履行1個(gè)類(lèi)型為kGcTypeFull的、要求回收那些只被軟援用對(duì)象援用的對(duì)象的GC,接著再在允許范圍內(nèi)增長(zhǎng)堆大小的條件下嘗試分配內(nèi)存。這1次如果還是失敗,那就真的是內(nèi)存不足了。

        至此,我們就對(duì)ART運(yùn)行時(shí)在堆上為新創(chuàng)建對(duì)象分配內(nèi)存的進(jìn)程分析完成了。從中我們就能夠看到,ART運(yùn)行時(shí)面臨的最大挑戰(zhàn)就是內(nèi)存不足問(wèn)題,它要通過(guò)在允許范圍內(nèi)增長(zhǎng)堆大小和垃圾回收兩個(gè)手段來(lái)解決。其中,垃圾回收會(huì)對(duì)程序造成影響,因此在履行垃圾回收時(shí),使用的力度要從小到大。在接下來(lái)的1篇文章中,我們就將詳細(xì)分析這些力度不同的垃圾回收是如何實(shí)現(xiàn)的,敬請(qǐng)關(guān)注!更多的信息也能夠關(guān)注老羅的新浪微博:http://weibo.com/shengyangluo。


生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)
程序員人生
------分隔線(xiàn)----------------------------
分享到:
------分隔線(xiàn)----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 国产丝袜一区二区三区在线观看 | 国产91一区二这在线播放 | 久久美女福利视频 | 日韩激情中文字幕一区二区 | 91麻豆精品国产91久久久久久 | 亚洲一区二区在线成人 | 午夜色站 | 欧美stockingssexxxx| 日本特黄a级高清免费酷网 日本特黄的免费大片视频 日本特黄高清免费大片爽 日本特黄色大片 | 精品三级视频 | 69性视频 | 欧美jizz19性欧美 | 精品成人一区二区三区免费视频 | 亚洲春色另类小说 | 欧洲色吧 | 亚洲天堂2016| 国产亚洲免费观看 | 日本一二三四区免费视频 | 成人在色线视频在线观看免费大全 | 欧美a级v片不卡在线观看 | 亚洲精品黄色 | 特级aav毛片日本免费视频 | 久久久精品456亚洲影院 | 综合九九 | 亚洲综合综合在线 | 亚洲视频 欧美视频 | 欧美日韩一区二区高清视 | 日本在线一区 | 精品国产免费第一区二区三区日韩 | 久久亚洲一级α片 | 18av黄动漫网站在线观看 | 网站视频免费 | 精品国产免费一区二区三区五区 | 中国女警察一级毛片视频 | 亚洲精品一区二区三区国产 | a毛片全部播放免费视频完整18 | 亚洲天堂小视频 | japanese日本护士18 | 在线观看www日本免费网站 | a色在线| 亚洲高清国产一线久久 |