Android ART虚拟机 Space类体系
Posted baiiu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android ART虚拟机 Space类体系相关的知识,希望对你有一定的参考价值。
前言
在ART虚拟机实现中,内存分配和释放的算法是封装在不同的Space中来完成的。而外部使用者只能借助Space及派生类的接口来完成内存的分配与释放。通过阅读这些Space的实现,可以看出ART虚拟机的一个重要的特点就是大量使用映射内存,相较于Dalvik虚拟机来说在内存分配上管理的更细致。
ART虚拟机提供了多种内存分配手段,它们分别由LargeObjectSpace、BumpPointerSpace、ZygoteSpace、RegionSpace、DlMallocSpace和RosAllocSpace六个类来实现,虚拟机内部会根据配置情况来使用不同的内存分配类,本文来仔细。
概览
art/runtime/gc/space/space.h
- 第一层包含Space和AllocSpace两个类。
Space代表一块内存空间,而纯虚类AllocSpace则代表一块可用于内存分配的空间。
AllocSpace提供了和内存分配及释放有关的虚函数,比如Alloc、Free等。 - 第二层包含ContinuousSpace和DiscontinuousSpace两个类。它们均派生自Space类。
ContinuousSpace表示一块地址连续的内存空间,
DiscontinuousSpace则表示一块地址不连续的空间。 - 第三层包含MemMapSpace和LargeObjectSpace两个类。
MemMapSpace派生自ContinuousSpace,它表示内存空间里的内存是通过内存映射技术来提供的。
LargeObjectSpace同时派生自DiscontinuousSpace和AllocSpace。该空间里的内存资源可以分配给外部使用。在ART虚拟机中,如果一个Java对象(注意,该对象的类型必须为Java String或基础数据的数组类型,比如int数组)所需内存超过3个内存页时,将使用LargeObjectSpace来提供内存资源。 - 第四层包含ImageSpace和ContinuousMemMapAllocSpace两个类。
ImageSpace用于.art文件的加载。一个ImageSpace创建成功后,其对应的.art文件里所包含的mirror Object对象就算创建完毕并加载到内存里了。
ContinuousMemMapAllocSpace代表一个可对外提供连续内存资源的空间,其内存资源由内存映射技术提供。 - 第五层包含BumpPointerSpace、ZygoteSpace、RegionSpace和MallocSpace四个类。
其中只有MallocSpace是虚类,而其他三个类可直接用于分配内存资源,但所使用的内存分配算法各不相同。 - 第六层包含DlMallocSpace和RosAllocSpace两个类,它们派生自MallocSpace。这两个类也用于内存分配,只不过使用了不同的内存分配算法。
class Space;
class AllocSpace;
class ContinuousSpace : public Space ...
class MemMapSpace : public ContinuousSpace ...
class ImageSpace : public MemMapSpace ...
class ContinuousMemMapAllocSpace : public MemMapSpace, public AllocSpace ...
class ZygoteSpace FINAL : public ContinuousMemMapAllocSpace ...
class BumpPointerSpace FINAL : public ContinuousMemMapAllocSpace ...
class RegionSpace FINAL : public ContinuousMemMapAllocSpace ...
class MallocSpace : public ContinuousMemMapAllocSpace ...
class DlMallocSpace : public MallocSpace ...
class RosAllocSpace : public MallocSpace ...
class DiscontinuousSpace : public Space ...
class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace ...
enum SpaceType
kSpaceTypeImageSpace,
kSpaceTypeMallocSpace,
kSpaceTypeZygoteSpace,
kSpaceTypeBumpPointerSpace,
kSpaceTypeLargeObjectSpace,
kSpaceTypeRegionSpace,
;
enum GcRetentionPolicy
// Objects are retained forever with this policy for a space.
kGcRetentionPolicyNeverCollect,
// Every GC cycle will attempt to collect objects in this space.
kGcRetentionPolicyAlwaysCollect,
// Objects will be considered for collection only in "full" GC cycles, ie faster partial collections won't scan these areas such as the Zygote.
kGcRetentionPolicyFullCollect,
;
// A space contains memory allocated for managed objects.
class Space
public:
// The policy of when objects are collected associated with this space.
GcRetentionPolicy GetGcRetentionPolicy() const
return gc_retention_policy_;
// Is the given object contained within this space?
virtual bool Contains(const mirror::Object* obj) const = 0;
// The kind of space this: image, alloc, zygote, large object.
virtual SpaceType GetType() const = 0;
// Returns true if objects in the space are movable.
virtual bool CanMoveObjects() const = 0;
virtual ~Space()
protected:
Space(const std::string& name, GcRetentionPolicy gc_retention_policy);
void SetGcRetentionPolicy(GcRetentionPolicy gc_retention_policy)
gc_retention_policy_ = gc_retention_policy;
// Name of the space that may vary due to the Zygote fork.
std::string name_;
protected:
GcRetentionPolicy gc_retention_policy_;
// AllocSpace interface.
class AllocSpace
public:
// Number of bytes currently allocated.
virtual uint64_t GetBytesAllocated() = 0;
// Number of objects currently allocated.
virtual uint64_t GetObjectsAllocated() = 0;
// Allocate num_bytes without allowing growth. If the allocation
// succeeds, the output parameter bytes_allocated will be set to the
// actually allocated bytes which is >= num_bytes.
// Alloc can be called from multiple threads at the same time and must be thread-safe.
//
// bytes_tl_bulk_allocated - bytes allocated in bulk ahead of time for a thread local allocation,
// if applicable. It can be
// 1) equal to bytes_allocated if it's not a thread local allocation,
// 2) greater than bytes_allocated if it's a thread local
// allocation that required a new buffer, or
// 3) zero if it's a thread local allocation in an existing
// buffer.
// This is what is to be added to Heap::num_bytes_allocated_.
virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
size_t* usable_size, size_t* bytes_tl_bulk_allocated) = 0;
// Thread-unsafe allocation for when mutators are suspended, used by the semispace collector.
virtual mirror::Object* AllocThreadUnsafe(Thread* self, size_t num_bytes, size_t* bytes_allocated,
size_t* usable_size,
size_t* bytes_tl_bulk_allocated)
REQUIRES(Locks::mutator_lock_)
return Alloc(self, num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated);
// Return the storage space required by obj.
virtual size_t AllocationSize(mirror::Object* obj, size_t* usable_size) = 0;
// Returns how many bytes were freed.
virtual size_t Free(Thread* self, mirror::Object* ptr) = 0;
// Returns how many bytes were freed.
virtual size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) = 0;
// Revoke any sort of thread-local buffers that are used to speed up allocations for the given
// thread, if the alloc space implementation uses any.
// Returns the total free bytes in the revoked thread local runs that's to be subtracted
// from Heap::num_bytes_allocated_ or zero if unnecessary.
virtual size_t RevokeThreadLocalBuffers(Thread* thread) = 0;
// Revoke any sort of thread-local buffers that are used to speed up allocations for all the
// threads, if the alloc space implementation uses any.
// Returns the total free bytes in the revoked thread local runs that's to be subtracted
// from Heap::num_bytes_allocated_ or zero if unnecessary.
virtual size_t RevokeAllThreadLocalBuffers() = 0;
virtual void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) = 0;
protected:
struct SweepCallbackContext
SweepCallbackContext(bool swap_bitmaps, space::Space* space);
const bool swap_bitmaps;
space::Space* const space;
Thread* const self;
collector::ObjectBytePair freed;
;
AllocSpace()
virtual ~AllocSpace()
;
父类
ContinuousSpace类
// Continuous spaces have bitmaps, and an address range. Although not required, objects within
// continuous spaces can be marked in the card table.
class ContinuousSpace : public Space
private:
// The beginning of the storage for fast access.
// Address at which the space begins.
uint8_t* begin_;
// Current end of the space.
// Current address at which the space ends, which may vary as the space is filled.
Atomic<uint8_t*> end_;
// Limit of the space.
// The end of the address range covered by the space.
uint8_t* limit_;
public:
// Current size of space
size_t Size() const
return End() - Begin();
// Maximum which the mapped space can grow to.
virtual size_t Capacity() const
return Limit() - Begin();
// pure virtual functions
virtual accounting::ContinuousSpaceBitmap* GetLiveBitmap() const = 0;
virtual accounting::ContinuousSpaceBitmap* GetMarkBitmap() const = 0;
DiscontinuousSpace类
// Required object alignment
static constexpr size_t kObjectAlignment = 8;
static constexpr size_t kLargeObjectAlignment = kPageSize;
typedef SpaceBitmap<kObjectAlignment> ContinuousSpaceBitmap;
typedef SpaceBitmap<kLargeObjectAlignment> LargeObjectBitmap;
// A space where objects may be allocated higgledy-piggledy throughout virtual memory.
// Currently the card table can't cover these objects and so the write barrier shouldn't be triggered. // This is suitable for use for large primitive arrays.
class DiscontinuousSpace : public Space
public:
accounting::LargeObjectBitmap* GetLiveBitmap() const
return live_bitmap_.get();
accounting::LargeObjectBitmap* GetMarkBitmap() const
return mark_bitmap_.get();
virtual bool IsDiscontinuousSpace() const OVERRIDE
return true;
virtual ~DiscontinuousSpace()
protected:
DiscontinuousSpace(const std::string& name, GcRetentionPolicy gc_retention_policy);
std::unique_ptr<accounting::LargeObjectBitmap> live_bitmap_;
std::unique_ptr<accounting::LargeObjectBitmap> mark_bitmap_;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(DiscontinuousSpace);
;
// 该父类初始化时即初始化了live_bitmap_、mark_bitmap_
DiscontinuousSpace::DiscontinuousSpace(const std::string& name, GcRetentionPolicy gc_retention_policy) : Space(name, gc_retention_policy)
const size_t capacity = static_cast<size_t>(std::numeric_limits<uint32_t>::max());
live_bitmap_.reset(accounting::LargeObjectBitmap::Create("large live objects", nullptr, capacity));
mark_bitmap_.reset(accounting::LargeObjectBitmap::Create("large marked objects", nullptr, capacity));
MemMapSpace
内部持有mem_map_
class MemMapSpace : public ContinuousSpace
protected:
// Underlying storage of the space
std::unique_ptr<MemMap> mem_map_;
;
ContinuousMemMapAllocSpace
针对ContinuousMemMapAllocSpace中的三个位图成员变量,其子类中:
- ZygoteSpace会设置live_bitmap_和mark_bitmap_。注意,这两个成员变量的值由外部传入,并非由ZygoteSpace自己创建。
- BumpPointerSpace和RegionSpace不设置这三个成员变量。
- DlMallocSpace和RosAllocSpace在它们的基类MallocSpace构造函数中初始化live_bitmap_和mark_bitmap_成员变量。
// Required object alignment
static constexpr size_t kObjectAlignment = 8;
static constexpr size_t kLargeObjectAlignment = kPageSize;
typedef SpaceBitmap<kObjectAlignment> ContinuousSpaceBitmap;
typedef SpaceBitmap<kLargeObjectAlignment> LargeObjectBitmap;
// Used by the heap compaction interface to enable copying from one type of alloc space to another.
class ContinuousMemMapAllocSpace : public MemMapSpace, public AllocSpace
protected:
std::unique_ptr<accounting::ContinuousSpaceBitmap> live_bitmap_;
std::unique_ptr<accounting::ContinuousSpaceBitmap> mark_bitmap_;
std::unique_ptr<accounting::ContinuousSpaceBitmap> temp_bitmap_;
MallocSpace
在构造函数中初始化live_bitmap_和mark_bitmap_成员变量。
// A common parent of DlMallocSpace and RosAllocSpace.
class MallocSpace : public ContinuousMemMapAllocSpace
protected:
// The growth_limit_ is used as the capacity of the alloc_space_ 内存分配的最高水位线,最大可分配内存不允许超过growth_limit_
size_t growth_limit_;
// True if objects in the space are movable. 和gc相关
bool can_move_objects_;
// Starting and initial sized, used when you reset the space.
const size_t starting_size_;
const size_t initial_size_;
// 全局静态变量
size_t MallocSpace::bitmap_index_ = 0;
MallocSpace::MallocSpace(const std::string& name, MemMap* mem_map, uint8_t* begin, uint8_t* end, uint8_t* limit, size_t growth_limit, bool create_bitmaps, bool can_move_objects, size_t starting_size, size_t initial_size)
: ContinuousMemMapAllocSpace(name, mem_map, begin, end, limit, kGcRetentionPolicyAlwaysCollect), recent_free_pos_(0), lock_("allocation space lock", kAllocSpaceLock), growth_limit_(growth_limit), can_move_objects_(can_move_objects), starting_size_(starting_size), initial_size_(initial_size)
if (create_bitmaps)
size_t bitmap_index = bitmap_index_++;
static const uintptr_t kGcCardSize = static_cast<uintptr_t>(accounting::CardTable::kCardSize);
live_bitmap_.reset(accounting::ContinuousSpaceBitmap::Create(
StringPrintf("allocspace %s live-bitmap %d", name.c_str(), static_cast<int>(bitmap_index)),
Begin(), NonGrowthLimitCapacity()));
mark_bitmap_.reset(accounting::ContinuousSpaceBitmap::Create(
StringPrintf("allocspace %s mark-bitmap %d", name.c_str(), static_cast<int>(bitmap_index)),
Begin(), NonGrowthLimitCapacity()));
for (auto& freed : recent_freed_objects_)
freed.first = nullptr;
freed.second = nullptr;
具体实现类
ZygoteSpace
An zygote space is a space which you cannot allocate into or free from.
ZygoteSpace虽然继承了AllocSpace,但它并没有真正提供内存分配和回收的功能。
art/runtime/gc/space/zygote_space.h
art/runtime/gc/space/zygote_space.cc
class ZygoteSpace FINAL : public ContinuousMemMapAllocSpace ...
ZygoteSpace* ZygoteSpace::Create(const std::string& name, MemMap* mem_map,
accounting::ContinuousSpaceBitmap* live_bitmap,
accounting::ContinuousSpaceBitmap* mark_bitmap)
size_t objects_allocated = 0;
CountObjectsAllocated visitor(&objects_allocated);
ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(mem_map->Begin()),
reinterpret_cast<uintptr_t>(mem_map->End()), visitor);
ZygoteSpace* zygote_space = new ZygoteSpace(name, mem_map, objects_allocated);
zygote_space->live_bitmap_.reset(live_bitmap);
zygote_space->mark_bitmap_.reset(mark_bitmap);
return zygote_space;
ZygoteSpace::ZygoteSpace(const std::string& name, MemMap* mem_map, size_t objects_allocated)
: ContinuousMemMapAllocSpace(name, mem_map, mem_map->Begin(), mem_map->End(), mem_map->End(), kGcRetentionPolicyFullCollect),
objects_allocated_(objects_allocated)
void ZygoteSpace::Clear()
UNIMPLEMENTED(FATAL);
UNREACHABLE();
mirror::Object* ZygoteSpace::Alloc(Thread*, size_t, size_t*, size_t*, size_t*)
UNIMPLEMENTED(FATAL);
UNREACHABLE();
size_t ZygoteSpace::AllocationSize(mirror::Object*, size_t*)
UNIMPLEMENTED(FATAL);
UNREACHABLE();
size_t ZygoteSpace::Free(Thread*, mirror::Object*)
UNIMPLEMENTED(FATAL);
UNREACHABLE();
size_t ZygoteSpace::FreeList(Thread*, size_t, mirror::Object**)
UNIMPLEMENTED(FATAL);
UNREACHABLE();
BumpPointerSpace
-
BumpPointerSpace提供了一种极其简单的内存分配算法——顺序分配(英文叫Sequential Allocation或Linear Allocation)。即BumpPointerSpace内存分配逻辑就是第N次内存分配的起始位置为第N-1次内存分配的终点位置。所以,算法中只要有一个变量记住最后一次分配的终点位置即可,这个位置就叫Bump Pointer。
-
正因为BumpPointerSpace采用了如此简单的内存分配算法,所以它压根就不能释放某一次所分配的内存(和ZygoteSpace一样,Free等函数没有真正的实现),而只支持一次性释放所有已分配的内存(实现了AllocSpace的Clear函数,详情见下文代码分析)
-
如此简单(换一种角度来说就是非常高效)的内存分配和释放算法使得BumpPointer-Space非常适合做线程本地内存分配——Thread Local Allocation Blocks,简写为TLAB,它代表一块专属某个线程的内存资源。
-
BumpPointerSpace提供了两种内存分配方法。
Alloc用于为某个mirror Object对象分配所需的内存。
AllocNewTlab:当ART虚拟机决定从调用线程的本地存储空间中分配内存时将调用此函数。 -
ART虚拟机里为每个Thread对象分配TLAB的方式:
Heap类中有一个名为bump_pointer_space_成员变量,它指向一个BumpPointerSpace对象。而这个BumpPointerSpace对应的内存空间可以被任意一个线程作为TLAB来使用。
第一个分配TLAB的线程将创建一个Main block。Main block位于内存资源的头部。其尾部位置由main_block_size_指明。
后续线程的TLAB都会有一个BlockHeader来描述。
art/runtime/gc/space/bump_pointer_space-inl.h
art/runtime/gc/space/bump_pointer_space.h
art/runtime/gc/space/bump_pointer_space.cc
class
ART 和 Dalvik
Dalvik虚拟机 - Android5.0之前
1.Android中的虚拟机是Dalvik/ART
2.每个应用程序都对应有一个单独的Dalvik虚拟机实例。
3.Dalvik虚拟机实则也算是一个Java虚拟机,只不过它执行的不是class文件,而是dex文件。
4.Jvm是的指令集是基于堆栈。Dalvik的指令集是基本寄存器的
基于栈的虚拟机 - JVM
对于基于栈的虚拟机来说,每一个运行时的线程,都有一个独立的栈。
栈中记录了方法调用的历史,每有一次方法调用,栈中便会多一个栈桢。
最顶部的栈桢称作当前栈桢,其代表着当前执行的方法。
基于栈的虚拟机通过操作数栈进行所有操作。
执行过程啥的详见JVM - 深度解析
基于寄存器的虚拟机 - Dalvik
寄存器:CPU中高速存储的内存。
寄存器的虚拟机:实际上就是把栈帧上的局部变量表和操作数栈合二为一,省去了操作数栈里频繁的进栈出栈。
基于寄存器的虚拟机中没有操作数栈,但是有很多虚拟寄存器。其实和操作数栈相同,这些寄存器也存放在运行时栈中,本质上就是一个数组。与JVM相似,在Dalvik VM中每个线程都有自己的PC和调用栈,方法调用的活动记录以帧为单位保存在调用栈上。
都是直接计算直接填
第一步!
第二步!
第三步
优点:与JVM相比指令数明显变少了,数据的移动变少了(操作数栈里频繁的入栈出栈没了)
ART虚拟机 - Android5.0之后
ART虚拟机是Dalvik虚拟机的升级版,Android5.0之后默认都是ART虚拟机。
Dalvik虚拟机执行的是dex字节码, 解释执行 。从Android 2.2版本开始,支持 JIT即时编译(Just In Time)。 在程序运行的过程中进行选择热点代码(经常执行的代码)进行编译或者优化。
ART虚拟机执行的是本地机器码。android4.4引入,5.0成默认
解释执行(Dalvik)与编译执行(ART):
优缺点:
解释执行:(边执行边翻译)启动快,占用内存小,但是运行慢
翻译执行:(先全翻译,再执行)启动慢,占用内存大,但是运行快!
Android N之前 — ART的预编译机制(AOT)
ART 引入了 预先编译机制(Ahead Of Time) ,在安装时,ART 使用设备自带的 dex2oat 工具来编译应用, dex中的字节码将被编译成本地机器码 。
— 所以在android 5-6的机子上安装apk明显感觉变慢了,后来又改了,所以又变快了
)
Android N之后
Android N之前,ART使用预编译机制(AOT),在安装的时候编译,所以安装APK很慢。但是Android N之后,ART使用AOT编译,解释和即时编译(JIT)三者结合的方式。
安装的时候不进行预编译(AOT)、运行时解释执行、经常跑的代码即时编译(JIT)并将结果存到Profile配置文件中、手机闲置(充电)的时候进行预编译(AOT)
ClassLoader
ClassLoader类加载器负责把class代码加载到JVM中执行
ClassLoader是一个抽象类,他的子类中有两个重要的分别是BootClassLoader和PathClassLoader
BootClassLoader:用来加载系统的类,ArrayList,String,Map等等
PathClassLoader:用来加载Andriod自己的类和自定义的类。
双亲委托机制
直接看源码,加载一个类。
- 先看PathClassLoader(用来加载自定义类的)
2. 啥也没有,所以看父类
3. 还是啥也没有,继续看父类
父类ClassLoader里有一个loadClass,就是加载类
- loadClass的步骤
这就是双亲委托机制,加载新类的时候,先去parent加载,parent没取到再去BootClassLoader加载。最后都没有使用自己加载
双亲委托机制:1.避免重复加载,当父类已经加载过了,直接从父类的缓存里取。(调用parent.loadClass,会走父类的第一步findLoadedClass(name)。2.安全,可以防止系统API被篡改。(自己定义的String类就算包名类名全一样,还是会走系统的String(BootClassLoader)))
parent:构造函数自己传进来,没传默认是BootClassLoader
- 如果没有找到调用自己的c.findClass(name)
用图文表示:
热修复
根据上面ClassLoader加载的源码可以看出来,ClassLoader加载自定义类的时候,是通过将dex文件转成 dexElement数组,然后通过for循环逐一除去DexFile,并通过loadClassBinaryName()比较类名,取出来的。 所以热修复可以通过我们自己生成一个差异包,.dex文件,然后把他放到apk数组里,并通过反射将他放到dexElement数组的第一的位置,这样找class时,就会先找我们生成的。dex文件了。
最后
小编在网上收集了一些 Android 开发相关的学习文档、面试题、Android 核心笔记等等文档,希望能帮助到大家学习提升,如有需要参考的可以直接点击这里免费领取