ART运行时CompactingGC为新创建对象分配内存的过程分析.doc_第1页
ART运行时CompactingGC为新创建对象分配内存的过程分析.doc_第2页
ART运行时CompactingGC为新创建对象分配内存的过程分析.doc_第3页
ART运行时CompactingGC为新创建对象分配内存的过程分析.doc_第4页
ART运行时CompactingGC为新创建对象分配内存的过程分析.doc_第5页
已阅读5页,还剩32页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

ART运行时Compacting GC为新创建对象分配内存的过程分析在引进Compacting GC后,ART运行时优化了堆内存分配过程。最显著特点是为每个ART运行时线程增加局部分配缓冲区(Thead Local Allocation Buffer)和在OOM前进行一次同构空间压缩(Homogeneous Space Compact)。前者可提高堆内存分配效率,后者可解决内存碎片问题。本文就对ART运行时引进Compacting GC后的堆内存分配过程进行分析。从接口层面上看,除了提供常规的对象分配接口AllocObject,ART运行时的堆还提供了一个专门用于分配非移动对象的接口AllocNonMovableObject,如图1所示:非移动对象指的是保存在前面一篇文章提到的Non-Moving Space的对象,主要包括那些在类加载过程中创建的类对象(Class)、类方法对象(ArtMethod)和类成员变量对象(ArtField)等,以及那些在经历过若干次Generational Semi-Space GC之后仍然存活的对象。前者是通过AllocNonMovableObject接口分配的,而后者是在执行Generational Semi-Space GC过程移动过去的。本文主要关注通过AllocNonMovableObject接口分配的非移动对象。 无论是通过AllocObject接口分配对象,还是通过AllocNonMovableObject接口分配对象,最后都统一调用了另外一个接口AllocObjectWithAllocator进行具体的分配过程,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片class Heap public: . / Allocates and initializes storage for an object instance. template mirror:Object* AllocObject(Thread* self, mirror:Class* klass, size_t num_bytes, const PreFenceVisitor& pre_fence_visitor) SHARED_LOCKS_REQUIRED(Locks:mutator_lock_) return AllocObjectWithAllocator(self, klass, num_bytes, GetCurrentAllocator(), pre_fence_visitor); template mirror:Object* AllocNonMovableObject(Thread* self, mirror:Class* klass, size_t num_bytes, const PreFenceVisitor& pre_fence_visitor) SHARED_LOCKS_REQUIRED(Locks:mutator_lock_) return AllocObjectWithAllocator(self, klass, num_bytes, GetCurrentNonMovingAllocator(), pre_fence_visitor); template ALWAYS_INLINE mirror:Object* AllocObjectWithAllocator( Thread* self, mirror:Class* klass, size_t byte_count, AllocatorType allocator, const PreFenceVisitor& pre_fence_visitor) SHARED_LOCKS_REQUIRED(Locks:mutator_lock_); AllocatorType GetCurrentAllocator() const return current_allocator_; AllocatorType GetCurrentNonMovingAllocator() const return current_non_moving_allocator_; . private: . / Allocator type. AllocatorType current_allocator_; const AllocatorType current_non_moving_allocator_; . ; 这五个函数定义在文件art/runtime/gc/heap.h 在Heap类的成员函数AllocObject和AllocNonMovableObject中,参数self描述的是当前线程,klass描述的是要分配的对象所属的类型,参数num_bytes描述的是要分配的对象的大小,最后一个参数pre_fence_visitor是一个回调函数,用来在分配对象完成后在当前执行路径中执行初始化操作,例如分配完成一个数组对象,通过该回调函数立即设置数组的大小,这样就可以保证数组对象的完整性和一致性,避免多线程环境下通过加锁来完成相同的操作。 Heap类的成员函数AllocObjectWithAllocator需要另外一个额外的类型为AllocatorType的参数来描述分配器的类型,也就是描述要在哪个空间分配对象。AllocatorType是一个枚举类型,它的定义如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片/ Different types of allocators. enum AllocatorType kAllocatorTypeBumpPointer, / Use BumpPointer allocator, has entrypoints. kAllocatorTypeTLAB, / Use TLAB allocator, has entrypoints. kAllocatorTypeRosAlloc, / Use RosAlloc allocator, has entrypoints. kAllocatorTypeDlMalloc, / Use dlmalloc allocator, has entrypoints. kAllocatorTypeNonMoving, / Special allocator for non moving objects, doesnt have entrypoints. kAllocatorTypeLOS, / Large object space, also doesnt have entrypoints. ; 这个枚举类型定义在文件/art/runtime/gc/allocator_type.h。 AllocatorType一共有六个值,它们的含义如下所示: kAllocatorTypeBumpPointer:表示在Bump Pointer Space中分配对象。 kAllocatorTypeTLAB:表示要在由Bump Pointer Space提供的线程局部分配缓冲区中分配对象。 kAllocatorTypeRosAlloc:表示要在Ros Alloc Space分配对象。 kAllocatorTypeDlMalloc:表示要在Dl Malloc Space分配对象。 kAllocatorTypeNonMoving:表示要在Non Moving Space分配对象。 kAllocatorTypeLOS:表示要在Large Object Space分配对象。 Heap类的成员函数AllocObject和AllocNonMovableObject使用的分配器类型分别是由成员变量current_allocator_和current_non_moving_allocator_决定的。前者的值与当前使用的GC类型有关。当GC类型发生变化时,就会调用Heap类的成员函数ChangeCollector来修改当前使用的GC,同时也会调用另外一个成员函数ChangeAllocator来修改Heap类的成员变量current_allocator_的值。由于ART运行时只有一个Non-Moving Space,因此后者的值就固定为kAllocatorTypeNonMoving。 Heap类的成员函数ChangeCollector的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void Heap:ChangeCollector(CollectorType collector_type) / TODO: Only do this with all mutators suspended to avoid races. if (collector_type != collector_type_) . collector_type_ = collector_type; gc_plan_.clear(); switch (collector_type_) case kCollectorTypeCC: / Fall-through. case kCollectorTypeMC: / Fall-through. case kCollectorTypeSS: / Fall-through. case kCollectorTypeGSS: gc_plan_.push_back(collector:kGcTypeFull); if (use_tlab_) ChangeAllocator(kAllocatorTypeTLAB); else ChangeAllocator(kAllocatorTypeBumpPointer); break; case kCollectorTypeMS: gc_plan_.push_back(collector:kGcTypeSticky); gc_plan_.push_back(collector:kGcTypePartial); gc_plan_.push_back(collector:kGcTypeFull); ChangeAllocator(kUseRosAlloc ? kAllocatorTypeRosAlloc : kAllocatorTypeDlMalloc); break; case kCollectorTypeCMS: gc_plan_.push_back(collector:kGcTypeSticky); gc_plan_.push_back(collector:kGcTypePartial); gc_plan_.push_back(collector:kGcTypeFull); ChangeAllocator(kUseRosAlloc ? kAllocatorTypeRosAlloc : kAllocatorTypeDlMalloc); break; default: LOG(FATAL) Unimplemented; . 这个函数定义在文件ime/gc/heap.cc中。 从这里我们就可以看到,对于Compacting GC,它们使用的分配器类型只可能为kAllocatorTypeTLAB或者kAllocatorTypeBumpPointer,取决定Heap类的成员变量use_tlab_的值。Heap类的成员变量use_tlab_的值默认为false,但是可以通过ART运行时启动选项-XX:UseTLAB来设置为true。对于Mark-Sweep GC来说,它们使用的分配器类型只可能为kAllocatorTypeRosAlloc或者kAllocatorTypeDlMalloc,取决于常量kUseRosAlloc的值。 此外,我们还可以看到,根据当前使用的GC不同,Heap类的成员变量gc_plan_会被设置为不同的值,用来表示在分配对象过程中遇到内存不足时,应该执行的GC粒度。对于Compacting GC来说,只有一种GC粒度可执行,那就是kGcTypeFull,实际上就是说对Bump Pointer Space的所有不可达对象进行回收。对于Mark-Sweep GC来说,有三种GC粒度可执行,分别是kGcTypeSticky、kGcTypePartial和kGcTypeFull。这三者的含义可以参考前面一文。后面我们继续对象分配过程时,也可以看到Heap类的成员变量gc_plan_的用途。 Heap类的成员函数ChangeAllocator的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void Heap:ChangeAllocator(AllocatorType allocator) if (current_allocator_ != allocator) . current_allocator_ = allocator; MutexLock mu(nullptr, *Locks:runtime_shutdown_lock_); SetQuickAllocEntryPointsAllocator(current_allocator_); . 这个函数定义在文件ime/gc/heap.cc中。 Heap类的成员函数ChangeAllocator除了设置成员变量current_allocator_的值之外,还会调用函数SetQuickAllocEntryPointsAllocator来修改提供给Native Code的用来分配对象的入口点函数,以便Native Code可以在ART运行时切换GC时使用正常的接口来分配对象。这里所谓的Native Code,就是APK在安装时通过翻译DEX字节码得到的本地机器指令。 了解了分配器的类型之后,接下来我们就继续分析Heap类的成员函数AllocObjectWithAllocator的实现,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片template inline mirror:Object* Heap:AllocObjectWithAllocator(Thread* self, mirror:Class* klass, size_t byte_count, AllocatorType allocator, const PreFenceVisitor& pre_fence_visitor) . if (kCheckLargeObject & UNLIKELY(ShouldAllocLargeObject(klass, byte_count) return AllocLargeObject(self, klass, byte_count, pre_fence_visitor); mirror:Object* obj; . if (allocator = kAllocatorTypeTLAB) byte_count = RoundUp(byte_count, space:BumpPointerSpace:kAlignment); if (allocator = kAllocatorTypeTLAB & byte_count TlabSize() obj = self-AllocTlab(byte_count); . obj-SetClass(klass); . pre_fence_visitor(obj, usable_size); . else obj = TryToAllocate(self, allocator, byte_count, &bytes_allocated, &usable_size); if (UNLIKELY(obj = nullptr) bool is_current_allocator = allocator = GetCurrentAllocator(); obj = AllocateInternalWithGc(self, allocator, byte_count, &bytes_allocated, &usable_size, &klass); if (obj = nullptr) bool after_is_current_allocator = allocator = GetCurrentAllocator(); / If there is a pending exception, fail the allocation right away since the next one / could cause OOM and abort the runtime. if (!self-IsExceptionPending() & is_current_allocator & !after_is_current_allocator) / If the allocator changed, we need to restart the allocation. return AllocObject(self, klass, byte_count, pre_fence_visitor); return nullptr; . obj-SetClass(klass); . pre_fence_visitor(obj, usable_size); . . if (AllocatorHasAllocationStack(allocator) PushOnAllocationStack(self, &obj); . if (AllocatorMayHaveConcurrentGC(allocator) & IsGcConcurrent() CheckConcurrentGC(self, new_num_bytes_allocated, &obj); . return obj; 这个函数定义在文件art/runtime/gc/heap-inl.h中。 Heap类的成员函数AllocObjectWithAllocator分配对象的主要逻辑如图2所示:首先,如果模板参数kCheckLargeObject等于true,并且要分配的是一个原子类型数组,且该为数组的大小大于预先设置的值,那么忽略掉参数allocator,而是调用Heap类的另外一个成员函数AllocLargeObject直接在Large Object Space中分配内存。后一个条件是通过调用Heap类的成员函数ShouldAllocLargeObject来判断是否满足的,它的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片inline bool Heap:ShouldAllocLargeObject(mirror:Class* c, size_t byte_count) const / We need to have a zygote space or else our newly allocated large object can end up in the / Zygote resulting in it being prematurely freed. / We can only do this for primitive objects since large objects will not be within the card table / range. This also means that we rely on SetClass not dirtying the objects card. return byte_count = large_object_threshold_ & c-IsPrimitiveArray(); 这个函数定义在文件art/runtime/gc/heap-inl.h中。 Heap类的成员变量large_object_threshold_初始化为kDefaultLargeObjectThreshold,后者又定义为3个内存页大小。也就是说,当分配的原子类型数组大小大于等于3个内存页时,就在Large Object Space中进行分配。 回到Heap类的成员AllocObjectWithAllocator中,如果指定了要在当前ART运行时线程的TLAB中分配对象,并且这时候当前ART运行时线程的TLAB的剩余大小大于请求分配的对象大小,那么就直接在当前线程的TLAB中分配。ART运行时线程的TLAB实际上是来自于Bump Pointer Space上的,后面我们就可以看到这一点。 如果上面的条件都不成立,接下来就调用Heap类的成员函数TryToAllocate来进行分配了。Heap类的成员函数TryToAllocate会根据参数allocator,在指定的Space分配内存,同时会根据第二个模板参数来决定是否要在允许的范围内增加Space的大小限制,以便可以满足分配要求。这里指定Heap类的成员函数TryToAllocate的值为false,就表示现在在不增长Space的大小限制的前提下为对象分配内存。 如果Heap类的成员函数TryToAllocate不能成功分配到指定大小的内存,那么就需要调用Heap类的成员函数AllocateInternalWithGc来先执行必要的GC,再尝试分配请求的内存了。 如果Heap类的成员函数AllocateInternalWithGc也不能成功分配到内存,那就表明是分配失败了。不过有一个例外,那就是ART运行时当前使用分配器类型发生了变化,这种情况就需要重新调用Heap类的成员函数AllocObject重启分配过程。从上面的分析可以知道,当ART运行时当前使用的GC发生切换时,ART运行时当前使用的分配器类型也会随着变化,因此这时候重新调用Heap类的成员函数AllocObject,就可以使用当前的分配器来分配对象。 假设前面成功分配了到指定的内存,接下来还有两件事情需要做。 第一件事情是调用Heap类的成员函数AllocatorHasAllocationStack判断参数allocator指定的分配器是否与ART运行时的Allocation Stack有关。如果有关的话,那么就需要将刚才成功分配到的对象通过调用Heap类的成员函数PushOnAllocationStack压入到ART运行时的Allocation Stack中,以便以后可以执行Sticky GC。关于Sticky GC,可以参考前面一文。 Heap类的成员函数AllocatorHasAllocationStack的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片class Heap public: . static ALWAYS_INLINE bool AllocatorHasAllocationStack(AllocatorType allocator_type) return allocator_type != kAllocatorTypeBumpPointer & allocator_type != kAllocatorTypeTLAB; . ; 这个函数定义在文件art/runtime/gc/heap.h中。 前面提到,ART运行时线程的TLAB是来自于Bump Pointer Space的,而Bump Pointer Space是与Compacting GC相关的,Allocation Stack是与Sticky GC相关的,这就意味着Compacting GC不会执行Sticky类型的GC。 第二件事情是调用Heap类的成员函数AllocatorMayHaveConcurrentGC判断参数allocator指定的分配器是否与Concurrent GC相关,并且当前使用的GC就是一个Concurrent GC。如果条件都成立的话,就调用Heap类的成员函数CheckConcurrentGC检查是否需要发起一个Concurrent GC请求。 Heap类的成员函数AllocatorMayHaveConcurrentGC的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片class Heap public: . static ALWAYS_INLINE bool AllocatorMayHaveConcurrentGC(AllocatorType allocator_type) return AllocatorHasAllocationStack(allocator_type); . ; 这个函数定义在文件art/runtime/gc/heap.h中。 Heap类的成员函数AllocatorMayHaveConcurrentGC的判断逻辑与上面分析的成员函数AllocatorHasAllocationStack是一样的,这就意味着目前提供的Compacting GC都是非Concurrent的。不过以后是会提供具有Concurrent功能的Compacting GC的,称为Concurrent Copying GC。 以上就是Heap类的成员函数AllocObjectWithAllocator的实现,接下来我们继续分析Heap类的成员函数TryToAllocate和AllocateInternalWithGc的实现,以便可以更好地了解ART运行时分配对象的过程。这也有利用我们后面分析ART运行时的Compacting GC的执行过程。 Heap类的成员函数TryToAllocate的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片template inline mirror:Object* Heap:TryToAllocate(Thread* self, AllocatorType allocator_type, size_t alloc_size, size_t* bytes_allocated, size_t* usable_size) if (allocator_type != kAllocatorTypeTLAB & UNLIKELY(IsOutOfMemoryOnAllocation(allocator_type, alloc_size) return nullptr; mirror:Object* ret; switch (allocator_type) case kAllocatorTypeBumpPointer: DCHECK(bump_pointer_space_ != nullptr); alloc_size = RoundUp(alloc_size, space:BumpPointerSpace:kAlignment); ret = bump_pointer_space_-AllocNonvirtual(alloc_size); if (LIKELY(ret != nullptr) *bytes_allocated = alloc_size; *usable_size = alloc_size; break; case kAllocatorTypeRosAlloc: if (kInstrumented & UNLIKELY(running_on_valgrind_) / If running on valgrind, we should be using the instrumented path. ret = rosalloc_space_-Alloc(self, alloc_size, bytes_allocated, usable_size); else DCHECK(!running_on_valgrind_); ret = rosalloc_space_-AllocNonvirtual(self, alloc_size, bytes_allocated, usable_size); break; case kAllocatorTypeDlMalloc: if (kInstrumented & UNLIKELY(running_on_valgrind_) / If running on valgrind, we should be using the instrumented path. ret = dlmalloc_space_-Alloc(self, alloc_size, bytes_allocated, usable_size); else DCHECK(!running_on_valgrind_); ret = dlmalloc_space_-AllocNonvirtual(self, alloc_size, bytes_allocated, usable_size); break; case kAllocatorTypeNonMoving: ret = non_moving_space_-Alloc(self, alloc_size, bytes_allocated, usable_size); break; case kAllocatorTypeLOS: ret = large_object_space_-Alloc(

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论