ART运行时Compacting GC为新创建对象分配内存的过程分析.docx

上传人:b****4 文档编号:27314443 上传时间:2023-06-29 格式:DOCX 页数:58 大小:208.87KB
下载 相关 举报
ART运行时Compacting GC为新创建对象分配内存的过程分析.docx_第1页
第1页 / 共58页
ART运行时Compacting GC为新创建对象分配内存的过程分析.docx_第2页
第2页 / 共58页
ART运行时Compacting GC为新创建对象分配内存的过程分析.docx_第3页
第3页 / 共58页
ART运行时Compacting GC为新创建对象分配内存的过程分析.docx_第4页
第4页 / 共58页
ART运行时Compacting GC为新创建对象分配内存的过程分析.docx_第5页
第5页 / 共58页
点击查看更多>>
下载资源
资源描述

ART运行时Compacting GC为新创建对象分配内存的过程分析.docx

《ART运行时Compacting GC为新创建对象分配内存的过程分析.docx》由会员分享,可在线阅读,更多相关《ART运行时Compacting GC为新创建对象分配内存的过程分析.docx(58页珍藏版)》请在冰豆网上搜索。

ART运行时Compacting GC为新创建对象分配内存的过程分析.docx

ART运行时CompactingGC为新创建对象分配内存的过程分析

ART运行时CompactingGC为新创建对象分配内存的过程分析

在引进CompactingGC后,ART运行时优化了堆内存分配过程。

最显著特点是为每个ART运行时线程增加局部分配缓冲区(TheadLocalAllocationBuffer)和在OOM前进行一次同构空间压缩(HomogeneousSpaceCompact)。

前者可提高堆内存分配效率,后者可解决内存碎片问题。

本文就对ART运行时引进CompactingGC后的堆内存分配过程进行分析。

从接口层面上看,除了提供常规的对象分配接口AllocObject,ART运行时的堆还提供了一个专门用于分配非移动对象的接口AllocNonMovableObject,如图1所示:

非移动对象指的是保存在前面一篇文章提到的Non-MovingSpace的对象,主要包括那些在类加载过程中创建的类对象(Class)、类方法对象(ArtMethod)和类成员变量对象(ArtField)等,以及那些在经历过若干次GenerationalSemi-SpaceGC之后仍然存活的对象。

前者是通过AllocNonMovableObject接口分配的,而后者是在执行GenerationalSemi-SpaceGC过程移动过去的。

本文主要关注通过AllocNonMovableObject接口分配的非移动对象。

无论是通过AllocObject接口分配对象,还是通过AllocNonMovableObject接口分配对象,最后都统一调用了另外一个接口AllocObjectWithAllocator进行具体的分配过程,如下所示:

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

classHeap{

public:

......

//Allocatesandinitializesstorageforanobjectinstance.

template

mirror:

:

Object*AllocObject(Thread*self,mirror:

:

Class*klass,size_tnum_bytes,

constPreFenceVisitor&pre_fence_visitor)

SHARED_LOCKS_REQUIRED(Locks:

:

mutator_lock_){

returnAllocObjectWithAllocator(self,klass,num_bytes,

GetCurrentAllocator(),

pre_fence_visitor);

}

template

mirror:

:

Object*AllocNonMovableObject(Thread*self,mirror:

:

Class*klass,size_tnum_bytes,

constPreFenceVisitor&pre_fence_visitor)

SHARED_LOCKS_REQUIRED(Locks:

:

mutator_lock_){

returnAllocObjectWithAllocator(self,klass,num_bytes,

GetCurrentNonMovingAllocator(),

pre_fence_visitor);

}

template

ALWAYS_INLINEmirror:

:

Object*AllocObjectWithAllocator(

Thread*self,mirror:

:

Class*klass,size_tbyte_count,AllocatorTypeallocator,

constPreFenceVisitor&pre_fence_visitor)

SHARED_LOCKS_REQUIRED(Locks:

:

mutator_lock_);

AllocatorTypeGetCurrentAllocator()const{

returncurrent_allocator_;

}

AllocatorTypeGetCurrentNonMovingAllocator()const{

returncurrent_non_moving_allocator_;

}

......

private:

......

//Allocatortype.

AllocatorTypecurrent_allocator_;

constAllocatorTypecurrent_non_moving_allocator_;

......

};

这五个函数定义在文件art/runtime/gc/heap.h

在Heap类的成员函数AllocObject和AllocNonMovableObject中,参数self描述的是当前线程,klass描述的是要分配的对象所属的类型,参数num_bytes描述的是要分配的对象的大小,最后一个参数pre_fence_visitor是一个回调函数,用来在分配对象完成后在当前执行路径中执行初始化操作,例如分配完成一个数组对象,通过该回调函数立即设置数组的大小,这样就可以保证数组对象的完整性和一致性,避免多线程环境下通过加锁来完成相同的操作。

Heap类的成员函数AllocObjectWithAllocator需要另外一个额外的类型为AllocatorType的参数来描述分配器的类型,也就是描述要在哪个空间分配对象。

AllocatorType是一个枚举类型,它的定义如下所示:

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

//Differenttypesofallocators.

enumAllocatorType{

kAllocatorTypeBumpPointer,//UseBumpPointerallocator,hasentrypoints.

kAllocatorTypeTLAB,//UseTLABallocator,hasentrypoints.

kAllocatorTypeRosAlloc,//UseRosAllocallocator,hasentrypoints.

kAllocatorTypeDlMalloc,//Usedlmallocallocator,hasentrypoints.

kAllocatorTypeNonMoving,//Specialallocatorfornonmovingobjects,doesn'thaveentrypoints.

kAllocatorTypeLOS,//Largeobjectspace,alsodoesn'thaveentrypoints.

};

这个枚举类型定义在文件/art/runtime/gc/allocator_type.h。

AllocatorType一共有六个值,它们的含义如下所示:

kAllocatorTypeBumpPointer:

表示在BumpPointerSpace中分配对象。

kAllocatorTypeTLAB:

表示要在由BumpPointerSpace提供的线程局部分配缓冲区中分配对象。

kAllocatorTypeRosAlloc:

表示要在RosAllocSpace分配对象。

kAllocatorTypeDlMalloc:

表示要在DlMallocSpace分配对象。

kAllocatorTypeNonMoving:

表示要在NonMovingSpace分配对象。

kAllocatorTypeLOS:

表示要在LargeObjectSpace分配对象。

Heap类的成员函数AllocObject和AllocNonMovableObject使用的分配器类型分别是由成员变量current_allocator_和current_non_moving_allocator_决定的。

前者的值与当前使用的GC类型有关。

当GC类型发生变化时,就会调用Heap类的成员函数ChangeCollector来修改当前使用的GC,同时也会调用另外一个成员函数ChangeAllocator来修改Heap类的成员变量current_allocator_的值。

由于ART运行时只有一个Non-MovingSpace,因此后者的值就固定为kAllocatorTypeNonMoving。

Heap类的成员函数ChangeCollector的实现如下所示:

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidHeap:

:

ChangeCollector(CollectorTypecollector_type){

//TODO:

Onlydothiswithallmutatorssuspendedtoavoidraces.

if(collector_type!

=collector_type_){

......

collector_type_=collector_type;

gc_plan_.clear();

switch(collector_type_){

casekCollectorTypeCC:

//Fall-through.

casekCollectorTypeMC:

//Fall-through.

casekCollectorTypeSS:

//Fall-through.

casekCollectorTypeGSS:

{

gc_plan_.push_back(collector:

:

kGcTypeFull);

if(use_tlab_){

ChangeAllocator(kAllocatorTypeTLAB);

}else{

ChangeAllocator(kAllocatorTypeBumpPointer);

}

break;

}

casekCollectorTypeMS:

{

gc_plan_.push_back(collector:

:

kGcTypeSticky);

gc_plan_.push_back(collector:

:

kGcTypePartial);

gc_plan_.push_back(collector:

:

kGcTypeFull);

ChangeAllocator(kUseRosAlloc?

kAllocatorTypeRosAlloc:

kAllocatorTypeDlMalloc);

break;

}

casekCollectorTypeCMS:

{

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中。

从这里我们就可以看到,对于CompactingGC,它们使用的分配器类型只可能为kAllocatorTypeTLAB或者kAllocatorTypeBumpPointer,取决定Heap类的成员变量use_tlab_的值。

Heap类的成员变量use_tlab_的值默认为false,但是可以通过ART运行时启动选项-XX:

UseTLAB来设置为true。

对于Mark-SweepGC来说,它们使用的分配器类型只可能为kAllocatorTypeRosAlloc或者kAllocatorTypeDlMalloc,取决于常量kUseRosAlloc的值。

此外,我们还可以看到,根据当前使用的GC不同,Heap类的成员变量gc_plan_会被设置为不同的值,用来表示在分配对象过程中遇到内存不足时,应该执行的GC粒度。

对于CompactingGC来说,只有一种GC粒度可执行,那就是kGcTypeFull,实际上就是说对BumpPointerSpace的所有不可达对象进行回收。

对于Mark-SweepGC来说,有三种GC粒度可执行,分别是kGcTypeSticky、kGcTypePartial和kGcTypeFull。

这三者的含义可以参考前面一文。

后面我们继续对象分配过程时,也可以看到Heap类的成员变量gc_plan_的用途。

Heap类的成员函数ChangeAllocator的实现如下所示:

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidHeap:

:

ChangeAllocator(AllocatorTypeallocator){

if(current_allocator_!

=allocator){

......

current_allocator_=allocator;

MutexLockmu(nullptr,*Locks:

:

runtime_shutdown_lock_);

SetQuickAllocEntryPointsAllocator(current_allocator_);

......

}

}

这个函数定义在文件ime/gc/heap.cc中。

Heap类的成员函数ChangeAllocator除了设置成员变量current_allocator_的值之外,还会调用函数SetQuickAllocEntryPointsAllocator来修改提供给NativeCode的用来分配对象的入口点函数,以便NativeCode可以在ART运行时切换GC时使用正常的接口来分配对象。

这里所谓的NativeCode,就是APK在安装时通过翻译DEX字节码得到的本地机器指令。

了解了分配器的类型之后,接下来我们就继续分析Heap类的成员函数AllocObjectWithAllocator的实现,如下所示:

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

template

inlinemirror:

:

Object*Heap:

:

AllocObjectWithAllocator(Thread*self,mirror:

:

Class*klass,

size_tbyte_count,AllocatorTypeallocator,

constPreFenceVisitor&pre_fence_visitor){

......

if(kCheckLargeObject&&UNLIKELY(ShouldAllocLargeObject(klass,byte_count))){

returnAllocLargeObject(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<=self->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)){

boolis_current_allocator=allocator==GetCurrentAllocator();

obj=AllocateInternalWithGc(self,allocator,byte_count,&bytes_allocated,&usable_size,

&klass);

if(obj==nullptr){

boolafter_is_current_allocator=allocator==GetCurrentAllocator();

//Ifthereisapendingexception,failtheallocationrightawaysincethenextone

//couldcauseOOMandaborttheruntime.

if(!

self->IsExceptionPending()&&is_current_allocator&&!

after_is_current_allocator){

//Iftheallocatorchanged,weneedtorestarttheallocation.

returnAllocObject(self,klass,byte_count,pre_fence_visitor);

}

returnnullptr;

}

}

......

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);

}

......

returnobj;

}

这个函数定义在文件art/runtime/gc/heap-inl.h中。

Heap类的成员函数AllocObjectWithAllocator分配对象的主要逻辑如图2所示:

首先,如果模板参数kCheckLargeObject等于true,并且要分配的是一个原子类型数组,且该为数组的大小大于预先设置的值,那么忽略掉参数allocator,而是调用Heap类的另外一个成员函数AllocLargeObject直接在LargeObjectSpace中分配内存。

后一个条件是通过调用Heap类的成员函数ShouldAllocLargeObject来判断是否满足的,它的实现如下所示:

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

inlineboolHeap:

:

ShouldAllocLargeObject(mirror:

:

Class*c,size_tbyte_count)const{

//Weneedtohaveazygotespaceorelseournewlyallocatedlargeobjectcanendupinthe

//Zygoteresultinginitbeingprematurelyfreed.

//Wecanonlydothisforprimitiveobjectssincelargeobjectswillnotbewithinthecardtable

//range.ThisalsomeansthatwerelyonSetClassnotdirtyingtheobject'scard.

returnbyte_count>=large_object_threshold_&&c->IsPrimitiveArray();

}

这个函数定义在文件art/runtime/gc/heap-inl.h中。

Heap类的成员变量large_object_threshold_初始化为kDefaultLargeObjectThreshold,后者又定义为3个内存页大小。

也就是说,当分配的原子类型数组大小大于等于3个内存页时,就在LargeObjectSpace中进行分配。

回到Heap类的成员AllocObjectWithAllocator中,如果指定了要在当前ART运行时线程的TLAB中分配对象,并且这时候当前ART运行时线程的TLAB的剩余大小大于请求分配的对象大小,那么就直接在当前线程的TLAB中分配。

ART运行时线程的TLAB实际上是来自于BumpPointerSpace上的,后面我们就可以看到这一点。

如果上面的条件都不成立,接下来就调用Heap类的成员函数TryToAllocate来进行分配了。

Heap类的成员函数TryToAllocate会根据参数allocator,在指定的Space分配内存,同时会根据第二个模板参数来决定

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 国外设计风格

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1