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自己的类和自定义的类。

双亲委托机制

直接看源码,加载一个类。

  1. 先看PathClassLoader(用来加载自定义类的)

2. 啥也没有,所以看父类

3. 还是啥也没有,继续看父类

父类ClassLoader里有一个loadClass,就是加载类

  1. loadClass的步骤

这就是双亲委托机制,加载新类的时候,先去parent加载,parent没取到再去BootClassLoader加载。最后都没有使用自己加载

双亲委托机制:1.避免重复加载,当父类已经加载过了,直接从父类的缓存里取。(调用parent.loadClass,会走父类的第一步findLoadedClass(name)。2.安全,可以防止系统API被篡改。(自己定义的String类就算包名类名全一样,还是会走系统的String(BootClassLoader)))

parent:构造函数自己传进来,没传默认是BootClassLoader

  1. 如果没有找到调用自己的c.findClass(name)

用图文表示:

热修复

根据上面ClassLoader加载的源码可以看出来,ClassLoader加载自定义类的时候,是通过将dex文件转成 dexElement数组,然后通过for循环逐一除去DexFile,并通过loadClassBinaryName()比较类名,取出来的。 所以热修复可以通过我们自己生成一个差异包,.dex文件,然后把他放到apk数组里,并通过反射将他放到dexElement数组的第一的位置,这样找class时,就会先找我们生成的。dex文件了。

最后

小编在网上收集了一些 Android 开发相关的学习文档、面试题、Android 核心笔记等等文档,希望能帮助到大家学习提升,如有需要参考的可以直接点击这里免费领取

在这里插入图片描述

以上是关于Android ART虚拟机 Space类体系的主要内容,如果未能解决你的问题,请参考以下文章

Android art模式解析

Android 虚拟机与类加载机制

Android Dalvik虚拟机和ART虚拟机对比

Android内存优化Dalvik虚拟机和ART虚拟机对比

笔记·Android体系与系统架构

安卓art虚拟机在啥位置