ART学习笔记(一)Space

ART学习笔记

Posted by Cc1over on November 28, 2019

ART学习笔记(一)Space

# 前言

隔了有一段时间没有去记录下学习的内容,心里感觉怪怪的,前段时间一直在忙着项目的开发,最近终于交接了,有时间来回顾一些学习的内容

# Space关键类

  • 第一层Space & AllocSpace: Space代表一块内存空间,而AllocSpace代表一块可用于内存分配的空间,提供了和内存分配及释放有关的虚函数

  • 第二层ContinuousSpace & DiscontinuousSpace: ContinuousSpace表示连续的内存空间 ,DiscontinuousSpace表示非连续的内存空间

  • 第三层MemMapSpace & LargeObjectSpace: MemMapSpace表示内存空间里的内存是通过内存映射技术来提供的,而当一个java对象(该对象的类型必须java String或基础数据类型,比如int数组)所需的内存超过3个内存页时,将使用LargeObjectSpace来提供内存资源

  • 第四层ImageSpace & ContinuousMemMapAllocSpace: ImageSpace用于.art文件的加载,ContinuousMemMapAllocSpace代表一个可以对外提供连续内存空间,其内存资源由内存映射技术提供

  • 第五层:只有MallocSpace是虚类,其他三个类都可以直接用于内存分配,但所使用的内存分配算法各不相同

  • 第六层:DlMallocSpace & RosAllocSpace 派生自MallocSpace,用于内存分配,但是使用了不同的算法

# ZygoteSpace

  • 外界传入mem_map指针,表示对应的内存资源
  • mirror Object对应于java中的Obect,8字节对齐
  • 不能分配内存及释放内存

# BumpPointerSpace

  • 采用顺序分配的分配方式,内存分配逻辑为第N次内存分配的起始位置是第N-1次内存分配的重点位置,因此需要记录下最后最后一次分配的终点位置 === Bump Pointer
  • 不能释放某一次所分配的内存,只支持一次性释放所有已分配的内存
  • 适合用于TLAB的分配

  • heap中有成员bump_pointer_space,指向一个BumpPointerSpace对象,可以被任意一个线程作为TLAB使用
  • 第一个分配TLAB的线程将创建一个Main Block,位于内存资源的头部,尾随main_block_size_指明
  • 后续线程的TLAB都会有一个BlockHeader描述

[bump_pointer_space-inl.h-> BumpPointerSpace::Alloc]

inline mirror::Object* BumpPointerSpace::Alloc(Thread* , size_t num_bytes, size_t* bytes_allocated, size_t* usable_size, size_t* bytes_tl_bulk_allocated) {
    //......
    // 8字节向上对齐
    nums_bytes = RoundUp(num_bytes, 8);
    // 分配内存,返回值类型为mirror Object*
    mirror::Object* ret = AllocNonvirtual(nums_bytes);
    // 设置返回参数,各种size的判读和设置
    // ......
    return ret;
}

[bump_pointer_space-inl.h-> BumpPointerSpace::AllocNonvirtual]

inline mirror::Object* BumpPointerSpace::AllocNonvirtual(size_t num_bytes) {
    // 交由AllocNonvirtualWithoutAccoutint进行内存分配
    mirror::Object* ret = AllocNonvirtualWithoutAccoutint(num_bytes);
    if(ret != nullptr){
        // object_allocated与bytes_allocated都是AtomicInteger类型,若对象分配成功,则添加两个计数器的计数值
        object_allocated_.FercgAbdAddSequentiallyConsistent(1);
        bytes_allocated_.FercgAbdAddSequentiallyConsistent(num_bytes)
    }
    return ret;
}

[bump_pointer_space-inl.h-> AllocNonvirtualWithoutAccoutint]

inline mirror::Object* BumpPointerSpace::AllocNonvirtualWithoutAccoutint(size_t num_bytes) {
    uint8_t* old_end;
    uint8_t* new_end;
    do{
        // end_为ContinousSpace的成员变量,表示上一次内存分配的末尾位置,就是Bump Pointer位置,为Atomic<uint_8*>
        // 获取当前space的末尾位置
        old_end = end_.LoadRelaxed();
        // 计算新的末尾位置
        new_end = old_end + num_byes;
        // 如果超过内存资源的大小,则返回空指针
        if(UNLIKELY(new_end > growth_end_)){
            return nullptr;
        }
        // 将end_指向新的末尾的位置
        while(!end_.CompareExchangeWeakSequentiallyConsistent(old_end,new_end));
    }
    return reinterpret_cast<mirror::Object*>(old_end);
}

[bump_pointer_space.cc-> BumpPointerSpace::AllocNewTlab]

bool BumpPointerSpace::AllocNewTlab(Thread* self, size_t bytes) {
    MutexLock mu(Thread::Current(),block_lock_);
    // 先释放self线程原来的TLAB
    RevokeThreadLocalBuffersLocked(self);
    uint8_t* start = AllocBlock(bytes);
    if(start == nullptr){
        return false;
    }
    self->setTlab(start,start+bytes);
    return true;
}

[Thread tlsPtr_]

struct PACKED(sizeof(void *)) tls_ptr_sized_values {
    // 表示 TLAB 中分配了多少个对象
    size_t thread_local_objects;
    // TLAB 的起始位置
    uint8_t* thread_local_start;
    // TLAB 当前所分配的内存位置,位于 thread_local_start 与 thread_local_end 之间
    uint8_t* thread_local_pos;
    // TLAB 的末尾位置
    uint8_t* thread_local_end;
} tlsPtr_;
 
  • tlsPtr_是Thread中的一个结构体,记录了一些与TLAB相关的信息
  • setTlab函数执行的操作就是更新tlsPtr_指向的数据

[bump_pointer_space.cc-> BumpPointerSpace::AllocBlock]

uint8_t* BumpPointerSpace::AllocBlock(size_t btyes) {
    // 8字节向上对齐
    btyes = RoundUp(btyes,8);
    
    struct BlockHeader {
        // 内存块总大小
        size_t size_;
        // 剩余内存空间
        size_t unused_;
    }
    
    // 第一次分配,设置main_block_size_的值
    if(!num_blocks_) {
        main_block_size_ = Size();
    }
    
    // 分配内存,原来所需内存大小 + BlockHeader结构的大小
    uint8_t* storage =  reinterpret_cast<uint8_t*>
        (AllocNonvirtualWithoutAccoutint(bytes+sizeof(BlockHeader)))
    // 设置BlockHeader的信息
    // 跳过header的部分返回    
    storage += sizeof(BlockHeader); 
    // blocks计数 + 1
    return storgae;
}

# RegionSpace

  • 先将内存资源划分成一个个固定的大小(默认为1MB)的内存块,每一个内存块由一个Region对象表示,进行内存分配时,先找到满足要求的Region,然后从Region中分配资源,Region内的分配方式与BumpPointerSpace的分配方式一致
  • RegionSpace的用法和拷贝垃圾回收算法的使用有关,拷贝垃圾回收算法把内存资源划分为两个semispace,新对象分配时,所需的内存来自其中一个semispace(tospace),而另一个semispace(fromspace)作为空闲空间,当tospace不够用的时候就进行垃圾回收,对两个space进行变量交换,然后将新fromspace中存活对象拷贝到新tospace中,最后释放fromspace的空间,而引入的Region的概念之后,对于内存资源的划分就可以不再局限于semispace,而是region,并且可以根据region的内存占用情况,动态设置region达到fromspace的条件

[region_space-inl.h-> RegionSpace::Alloc]

inline mirror::Object* RegionSpace::Alloc(Thread* , size_t num_bytes, size_t* byte_allocated, size_t* usbale_size, size_t* bytes_tl_bulk_allocated) {
    // 8字节向上对齐
    num_bytes = RoundUp(num_bytes,8);
    return AllocNonvirtual<false>(num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated);
}

[region_space-inl.h-> RegionSpace::AllocNonvirtual]

inline mirror::Object* RegionSpace::AllocNonvirtual(size_t num_bytes, size_t* bytes_allocated, size_t* usable_size, size_t* bytes_tl_bulk_allocated) {
    mirror::Object obj;
    // num_bytes与regionSize比较,过滤分配空间大于regionSize的情况
    if(num_bytes < = kRegionSize){
        // 不加锁根据kForEvac参数从当前region或者evac_region 分配内存
        // evac_regio代表还没分配过内存的region
        if(!kForEvac){
            obj = current_region->Alloc(num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated);
        }else{
            obj = evac_region->Alloc(num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated)
        }
       // 分配成功 
       if(obj != nullptr) return obj;
      
       // 留一半内存块作为fromspace
       if(num_non_free_regions_ + 1) * 2 > num_regions_) { 
          return nullptr;
       }  
        
       // 分配失败可能有两种原因:
       // 1) 并发问题:可能其他线程设置了current_region_
       // 在这种情况下就加锁重试 
       // 2) 非并发问题,遍历region数组找到空闲的region进行分配
       // 然后找到空闲的region之后便进行内存分配
        
    } else {
        obj = AllocLarge<kForEvac>(num_bytes,bytes_allocated,usable_size,bytes_tl_bulk_allocated);
        if(obj != nullptr){
            return obj;
        }
    }
    return nullptr;
}
  • RegionSpace Alloc的任务就是确认好目标的内存块,确认好目标的内存块后,真正的内存分配工作就交给Region的Alloc函数
  • 而Region的Alloc函数和BumpPointer使用的算法完全一样

[region_space.cc-> RegionSpace::AllocNewTlab]

RegionSpace也可用作线程的TLAB,regionSpace的AllocNewTlab的逻辑就是找到一个空闲的Region就可以了

# DlMallocSpace & RosAllocSpace

BumpPointerSpace & RegionSpace都采用了比较简单的内存分配算法,但是并不能满足类似在C语音中malloc和free这样自由分配和释放内存的要求,而MallocSpace这个虚类,提供了DlMallocSpaceRosAllocSpace两个实现类提供类似C语音中mallc/free的内存分配和释放功能

  • DlMallocSpace:使用开源的dlmalloc提供具体的内存分配和释放算法
  • RosAllocSpace:使用了谷歌开发的rosalloc内存分配器,而且需要ART虚拟机的其他模块配合使用,分配效果比dlmalloc更好

[heap.cc-> Heap::CreateMallocSpaceFromMemMap]

space::MallocSpace* Heap::CreateMallocSpaceFormMemMap(MemMap* mem_map, size_t initial_size, size_t growth_limit, size_t capacity, const char* name, bool can_move_objects) {
    // kUseRosAlloc默认为true
    if(kUseRosAlloc) {
        // 采用RosAllocSpace
    }else {
        // 采用DlMallocSpace
    }
    // 创建RememberSet, 主要用于存储跨Space引用关系,解决跨Space问题
}
  • 可见其实,ART虚拟机中的设计其实默认会使用RosAllocSpace,而定义虚类就是为了实现多态

# LargeObjectMapSpace

当一个对象所需的内存空间大小超过设定的阀值(默认为3个内存页),同时,该对象的类型必须是Java基础类型的数组或者Java对象的类型是java.lang.String时就会使用LargeObjectSpace,而且在Android高版本中,Fresco也会把Bitmap对象存放在LargeObjectMapSpace

LargeObjectMapSapce的常见并不需要和其他Space一样需要在构造函数传入mem_map,然后在mem_map进一步内存分配,而是调用Alloc函数的时候直接在操作系统中映射一块内存空间

[large_object_space.cc-> LargeObjectMapSpace::Alloc]

mirror::Object* LargeObjectMapSpace::Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated, size_t* usable_size, size_t* bytes_tl_bulk_allocated) {
    // 直接创建一个MemMap对象
    MemMap* mem_map = MemMap::MapAnonymous(num_bytes);
    // 将这块内存映射空间的基地址转换为返回值类型
    mirror::Object* const obj = reinterpret_cast<mirror::Object*>(mem_map->Begin());
    // 用一个map保存下内存映射空间的MemMap对象
    MutexLock mu(self, lock_);
    large_objects_.Put(obj, LargeObject {mem_map, false});
    return obj;
}

参考资料: 《深入理解Android虚拟机ART》