Hotspot 内存管理之Metaspace(一) 源码解析

目录

一、MetaspaceGC

1、initialize / post_initialize

2、inc_capacity_until_GC / dec_capacity_until_GC

3、compute_new_size

二、MetaspaceAux

1、定义

2、used_bytes / used_bytes_slow

3、inc_used / dec_used

4、MetaspacePool

三、 Metachunk / Metablock

1、定义

2、Metachunk::allocate

四、BlockFreelist

五、VirtualSpaceNode

1、定义

2、构造方法和initialize

3、get_chunk_vs

4、purge / retire


     在上一篇《Hotspot 内存管理之Universe 源码解析》中讲到Universe的初始化时会完成表示元空间的Metaspace的初始化,本篇就顺着Metaspace::global_initialize()方法的实现来学习Metaspace相关类的实现和调用关系。

一、MetaspaceGC

       MetaspaceGC并不是像类名一样用来对Metaspace执行GC的,仅仅用来维护属性_capacity_until_GC,当Metaspace的已分配内存值达到该属性就会触发GC,GC结束后_capacity_until_GC的值会增加直到达到参数MaxMetaspaceSize设置的Metaspace的最大值。MetaspaceGC的定义在hotspot src/shared/vm/memory/metaspace.hpp中,包含的属性只有三个,如下:

重点关注以下几个方法的实现。

1、initialize / post_initialize

      initialize是在universe_init方法中触发的,其调用链如下:

     post_initialize是在初始化完成后通过Metaspace::post_initialize方法触发的,其调用链如下:

    两方法都是设置_capacity_until_GC属性的值,其源码实现如下:

 其中配置项MaxMetaspaceSize表示元空间的最大值,默认是int类型的最大值,MetaspaceSize表示元空间的初始值,启用C2编译下默认是16M。

2、inc_capacity_until_GC / dec_capacity_until_GC

     这两方法就是用来增加和减少capacity_until_GC属性的值,其源码实现如下:


//尝试把_capacity_until_GC的值增加v,如果成功则返回true,如果其他线程并发的增加这个属性或者这个增加后的值超过了MaxMetaspaceSize都会
//失败返回false。如果成功,会可选的返回一个新的cap_until_GC的值和原来的旧的cap_until_GC的值,即设置到指针变量new_cap_until_GC和
//old_cap_until_GC中。如果失败,则会可选的设置can_retry指针变量,来表明是否存在足够的空间满足要求,如果有则调用方可以重新调用此方法
bool MetaspaceGC::inc_capacity_until_GC(size_t v, size_t* new_cap_until_GC, size_t* old_cap_until_GC, bool* can_retry) {
  //确认已经按照Metaspace内存分配粒度取整
  assert_is_size_aligned(v, Metaspace::commit_alignment());
  //原来的值
  size_t capacity_until_GC = (size_t) _capacity_until_GC;
  //新值
  size_t new_value = capacity_until_GC + v;
  if (new_value < capacity_until_GC) {
    //不会走到此分支
    new_value = align_size_down(max_uintx, Metaspace::commit_alignment());
  }
 
  if (new_value > MaxMetaspaceSize) {
    //大于最大值了,返回false
    if (can_retry != NULL) {
      *can_retry = false;
    }
    return false;
  }
  //小于最大值,返回true
  if (can_retry != NULL) {
    *can_retry = true;
  }

  
  intptr_t expected = (intptr_t) capacity_until_GC;
  //原子的修改属性
  intptr_t actual = Atomic::cmpxchg_ptr((intptr_t) new_value, &_capacity_until_GC, expected);
  //如果修改失败
  if (expected != actual) {
    return false;
  }
  //如果修改成功
  if (new_cap_until_GC != NULL) {
    *new_cap_until_GC = new_value;
  }
  if (old_cap_until_GC != NULL) {
    *old_cap_until_GC = capacity_until_GC;
  }
  return true;
}

size_t MetaspaceGC::dec_capacity_until_GC(size_t v) {
  assert_is_size_aligned(v, Metaspace::commit_alignment());
  //原子的减少属性_capacity_until_GC
  return (size_t)Atomic::add_ptr(-(intptr_t)v, &_capacity_until_GC);
}

inc_capacity_until_GC的调用链如下:

 dec_capacity_until_GC的调用链如下:

3、compute_new_size

     compute_new_size用于在GC完成后根据设置的最低空闲比例MinMetaspaceFreeRatio和最大的空闲比例MaxMetaspaceFreeRatio计算一个新的_capacity_until_GC属性值,实现动态调整Metaspace大小的功能,其调用链如下:

  都是垃圾回收器的相关实现调用此方法,该方法的源码实现如下:

void MetaspaceGC::compute_new_size() {
  assert(_shrink_factor <= 100, "invalid shrink factor");
  uint current_shrink_factor = _shrink_factor;
  _shrink_factor = 0;

  //committed_bytes()实际包含部分空闲的chunk块,即实际是未使用的,如果不包含他们会导致capacity_until_GC
  //缩减,过去曾因此导致了几个严重bug,所以这里依然把这些空闲的chunk块当做是使用中的内存
  const size_t used_after_gc = MetaspaceAux::committed_bytes();
  const size_t capacity_until_GC = MetaspaceGC::capacity_until_GC();
  
  //MinMetaspaceFreeRatio是GC完成后需要保证的Metaspace最低的空闲空间比例,默认是40,为了避免Metaspace因为内存不足再次触发GC
  const double minimum_free_percentage = MinMetaspaceFreeRatio / 100.0;
  const double maximum_used_percentage = 1.0 - minimum_free_percentage;

  //计算需要的最低内存值
  const double min_tmp = used_after_gc / maximum_used_percentage;
  //如果min_tmp大于MaxMetaspaceSize则取MaxMetaspaceSize,保证扩容后不超过最大值
  size_t minimum_desired_capacity =
    (size_t)MIN2(min_tmp, double(MaxMetaspaceSize));
  //如果MetaspaceSize大于minimum_desired_capacity则取MetaspaceSize,保证缩容后不低于初始值
  minimum_desired_capacity = MAX2(minimum_desired_capacity,
                                  MetaspaceSize);
  
  //打印GC日志
  if (PrintGCDetails && Verbose) {
    gclog_or_tty->print_cr("\nMetaspaceGC::compute_new_size: ");
    gclog_or_tty->print_cr("  "
                  "  minimum_free_percentage: %6.2f"
                  "  maximum_used_percentage: %6.2f",
                  minimum_free_percentage,
                  maximum_used_percentage);
    gclog_or_tty->print_cr("  "
                  "   used_after_gc       : %6.1fKB",
                  used_after_gc / (double) K);
  }


  size_t shrink_bytes = 0;
  //如果当前的capacity_until_GC小于期望值,则扩容,增加capacity_until_GC
  if (capacity_until_GC < minimum_desired_capacity) {
    //计算需要增加的值
    size_t expand_bytes = minimum_desired_capacity - capacity_until_GC;
    //取整
    expand_bytes = align_size_up(expand_bytes, Metaspace::commit_alignment());
    //MinMetaspaceExpansion表示扩容时最低的扩展值,默认是256k,低于此值不扩容
    if (expand_bytes >= MinMetaspaceExpansion) {
      size_t new_capacity_until_GC = 0;
      //增加capacity_until_GC
      bool succeeded = MetaspaceGC::inc_capacity_until_GC(expand_bytes, &new_capacity_until_GC);
      //在GC结束后的安全点调用此方法总是成功的
      assert(succeeded, "Should always succesfully increment HWM when at safepoint");
      //打印日志
      Metaspace::tracer()->report_gc_threshold(capacity_until_GC,
                                               new_capacity_until_GC,
                                               MetaspaceGCThresholdUpdater::ComputeNewSize);
      if (PrintGCDetails && Verbose) {
        gclog_or_tty->print_cr("    expanding:"
                      "  minimum_desired_capacity: %6.1fKB"
                      "  expand_bytes: %6.1fKB"
                      "  MinMetaspaceExpansion: %6.1fKB"
                      "  new metaspace HWM:  %6.1fKB",
                      minimum_desired_capacity / (double) K,
                      expand_bytes / (double) K,
                      MinMetaspaceExpansion / (double) K,
                      new_capacity_until_GC / (double) K);
      }
    }
    return;
  }

  //如果当前的capacity_until_GC大于最低期望值,需要判断capacity_until_GC是否大于最高期望值,如果是则缩容,减少capacity_until_GC
  //计算需要缩容的空间
  size_t max_shrink_bytes = capacity_until_GC - minimum_desired_capacity;
  assert(max_shrink_bytes >= 0, err_msg("max_shrink_bytes " SIZE_FORMAT,
    max_shrink_bytes));

  //MaxMetaspaceFreeRatio表示GC结束后Metaspace的最大空闲比例,默认是70
  if (MaxMetaspaceFreeRatio < 100) {
    //根据MaxMetaspaceFreeRatio计算允许的最大的空间值,不能低于MetaspaceSize初始值,不能大于最大值MaxMetaspaceSize
    const double maximum_free_percentage = MaxMetaspaceFreeRatio / 100.0;
    const double minimum_used_percentage = 1.0 - maximum_free_percentage;
    const double max_tmp = used_after_gc / minimum_used_percentage;
    size_t maximum_desired_capacity = (size_t)MIN2(max_tmp, double(MaxMetaspaceSize));
    maximum_desired_capacity = MAX2(maximum_desired_capacity,
                                    MetaspaceSize);
    //打印日志                                
    if (PrintGCDetails && Verbose) {
      gclog_or_tty->print_cr("  "
                             "  maximum_free_percentage: %6.2f"
                             "  minimum_used_percentage: %6.2f",
                             maximum_free_percentage,
                             minimum_used_percentage);
      gclog_or_tty->print_cr("  "
                             "  minimum_desired_capacity: %6.1fKB"
                             "  maximum_desired_capacity: %6.1fKB",
                             minimum_desired_capacity / (double) K,
                             maximum_desired_capacity / (double) K);
    }
    //合理校验,要求的最小内存值必须小于或者等于最大内存值
    assert(minimum_desired_capacity <= maximum_desired_capacity,
           "sanity check");
    
    //如果capacity_until_GC大于根据最大空闲比例计算出的允许的最大内存值,即当前的空间比例大于设置的最大比例,需要缩容
    if (capacity_until_GC > maximum_desired_capacity) {
      //计算需要缩容的大小
      shrink_bytes = capacity_until_GC - maximum_desired_capacity;
      //为了避免程序调用System.gc()触发的GC导致堆空间的再次分配,增加参数_shrink_factor,第一次GC时是0,即第一次GC时不会触发缩容
      //第二次是10,即最多只缩容10%,第三次是40%,第四次是100%
      shrink_bytes = shrink_bytes / 100 * current_shrink_factor;
      //内存取整
      shrink_bytes = align_size_down(shrink_bytes, Metaspace::commit_alignment());

      assert(shrink_bytes <= max_shrink_bytes,
        err_msg("invalid shrink size " SIZE_FORMAT " not <= " SIZE_FORMAT,
          shrink_bytes, max_shrink_bytes));
      //更新_shrink_factor
      if (current_shrink_factor == 0) {
        _shrink_factor = 10;
      } else {
        _shrink_factor = MIN2(current_shrink_factor * 4, (uint) 100);
      }
      //打印日志
      if (PrintGCDetails && Verbose) {
        gclog_or_tty->print_cr("  "
                      "  shrinking:"
                      "  initSize: %.1fK"
                      "  maximum_desired_capacity: %.1fK",
                      MetaspaceSize / (double) K,
                      maximum_desired_capacity / (double) K);
        gclog_or_tty->print_cr("  "
                      "  shrink_bytes: %.1fK"
                      "  current_shrink_factor: %d"
                      "  new shrink factor: %d"
                      "  MinMetaspaceExpansion: %.1fK",
                      shrink_bytes / (double) K,
                      current_shrink_factor,
                      _shrink_factor,
                      MinMetaspaceExpansion / (double) K);
      }
    }
  }

  //如果大于最低扩容空间,且缩容后大于初始值MetaspaceSize,则缩容
  if (shrink_bytes >= MinMetaspaceExpansion &&
      ((capacity_until_GC - shrink_bytes) >= MetaspaceSize)) {
    size_t new_capacity_until_GC = MetaspaceGC::dec_capacity_until_GC(shrink_bytes);
    Metaspace::tracer()->report_gc_threshold(capacity_until_GC,
                                             new_capacity_until_GC,
                                             MetaspaceGCThresholdUpdater::ComputeNewSize);
  }
}

二、MetaspaceAux

1、定义

      MetaspaceAux同样定义在metaspace.hpp中,它定义的属性和方法都是静态的,主要用于外部类获取Metaspace的内存使用情况,如获取Metaspace的当前最大容量的capacity_bytes方法,获取已使用空间大小的used_bytes方法,获取空闲的空间大小的free_bytes方法,获取已经分配内存的量的committed_bytes方法,获取保留的未分配内存的量的reserved_bytes方法。

MetaspaceAux定义的静态属性只有两个:

其中枚举值MetadataTypeCount对应的枚举的定义如下:

 MetadataType表示元数据的类型,即只有两种Class相关的数据和非Class相关的数据。设置这两个属性的目的是为了快速的获取指定类型的所有Metachunks的内存使用量和容量,避免遍历所有的classloaders,下面以used_bytes / used_bytes_slow方法的实现为例说明。

2、used_bytes / used_bytes_slow

      这两个方法都是获取Metaspace已使用内存大小的方法,前者是通过静态属性_used_words快速获取的,后者就是通过遍历所有的classloaders累加计算出来的,其实现如下:

 static size_t used_bytes() {
   //BytesPerWord表示一个字段对应的字节数
    return used_words() * BytesPerWord;
  }

  static size_t used_words() {
    return used_words(Metaspace::NonClassType) +
           used_words(Metaspace::ClassType);
  }

   static size_t used_words(Metaspace::MetadataType mdtype) {
    return _used_words[mdtype];
  }

  static size_t used_bytes_slow() {
    return used_bytes_slow(Metaspace::ClassType) +
           used_bytes_slow(Metaspace::NonClassType);
  }

  size_t MetaspaceAux::used_bytes_slow(Metaspace::MetadataType mdtype) {
  size_t used = 0;
  //ClassLoaderDataGraphMetaspaceIterator的构造方法会获取ClassLoaderData链表的头元素,从头元素开始依次遍历
  ClassLoaderDataGraphMetaspaceIterator iter;
  //遍历所有的ClassLoader
  while (iter.repeat()) {
    Metaspace* msp = iter.get_next();
    // Sum allocated_blocks_words for each metaspace
    if (msp != NULL) {
      used += msp->used_words_slow(mdtype);
    }
  }
  return used * BytesPerWord;
}

ClassLoaderDataGraphMetaspaceIterator的构造函数实现如下:

 ClassLoaderDataGraphMetaspaceIterator的repeat和get_next方法实现如下:

3、inc_used / dec_used

     这两方法就是用来增加和减少静态属性_used_words的,其源码实现如下:

其中的minus_words实际是一个负值。重点关注这两方法的调用链,如下:

 

这两方法的调用方都是SpaceManager,下一节会详细讲解该类的用途和实现。

4、MetaspacePool

     在universe_post_init方法中会调用MemoryService::add_metaspace_memory_pools方法给Metaspace创建一个对应的MemeryPool,但是该方法并没有像MemoryService::add_code_heap_memory_pool(CodeHeap* heap)一样把表示Metaspace的实例作为入参传给方法,那MetaspacePool是如何获取Metaspace的内存使用情况了?

   MemoryService::add_metaspace_memory_pools的实现如下:

 MetaspacePool是一个无参构造函数,答案在MetaspacePool改写了get_memory_usage方法的实现,如下图:

commited和used都是从MetaspaceAux中获取的,初始值就是构造函数传入的0,最大值就是calculate_max_size的返回值。 

三、 Metachunk / Metablock

1、定义

      Metachunk表示从一段连续的内存空间Virtualspace中分配的一小块内存,当Metachunk不在使用时会被添加到空闲链表中,从而被重新使用而不是释放其占用的内存。Metachunk和SpaceManager的关联关系不是固定的,即当Metachunk被重新使用时可能分配给一个新的SpaceManager。Metachunk的定义在hotspot src/shared/vm/memory/metachunk.hpp中,其类继承关系如下:

Metabase和Metablock的定义都在同一个文件metachunk.hpp中,Metabase抽象了Metachunk和Metablock添加到空闲链表或者空闲二叉树表(BinaryTreeDictionary)时需要的操作前后节点的相关属性和方法,其定义和实现都比较简单,如下:

Metablock直接继承自Metabase,没有添加新的方法或者属性,其定义如下:

 Metablock是从Metachunk中分配内存的单位,即从Metachunk中分配出去的内存块都是以Metablock的形式存在,Metablock可以被负责管理它的SpaceManager重复利用,并且与Metachunk不同的是,Metablock与SpaceManager的关联关系不会改变。MetaBlock构造方法的调用链如下:

Metachunk在Metabase的基础上新增了两个属性,如下图:

其中_container表示包含这个Metachunk的VirtualSpaceNode,即从哪个VirtualSpaceNode中分配的;MetaWord的定义和HeapWord的定义相同,如下:

 top属性表示已经未分配内存区域的起始地址,注意这里是以字段为单位,而不是字节,其内存示意图如下:

其中bottom就是Metachunk本身this的地址,end的地址根据Metachunk的大小计算而来。

2、Metachunk::allocate

Metachunk定义的方法都是围绕内存分配的,重点关注其构造方法和allocate方法的实现即可,如下:

Metachunk::Metachunk(size_t word_size,
                     VirtualSpaceNode* container)
    : Metabase<Metachunk>(word_size),
    _top(NULL),
    _container(container)
{
  _top = initial_top();
}

//返回除去保存Metachunk自身属性的那部分内存,可用于分配内存的起始地址
MetaWord* initial_top() const { return (MetaWord*)this + overhead(); }

size_t Metachunk::object_alignment() {
  //对象分配的粒度是8字节
  const size_t alignment = 8;
  //确保alignment和KlassAlignmentInBytes一致,KlassAlignmentInBytes表示分配Klass占用内存的粒度
  STATIC_ASSERT(alignment == (size_t)KlassAlignmentInBytes);

  return alignment;
}

//返回存储Metachunk本身相关属性需要占用的字宽数
size_t Metachunk::overhead() {
//将Metachunk本身的内存大小向上按照内存粒度取整,再除以一个字宽的字节数
  return align_size_up(sizeof(Metachunk), object_alignment()) / BytesPerWord;
}

MetaWord* Metachunk::allocate(size_t word_size) {
  MetaWord* result = NULL;
  //如果剩余可用空间充足则增加_top,将原来的_top返回,否则返回NULL
  if (free_word_size() >= word_size) {
    result = _top;
    _top = _top + word_size;
  }
  return result;
}

size_t Metachunk::free_word_size() const {
  return pointer_delta(end(), _top, sizeof(MetaWord));
}

uintptr_t* end() const        { return ((uintptr_t*) this) + size(); }

其中Metachunk构造方法的调用链如下:

 Metachunk::allocate方法的调用链如下:

四、BlockFreelist

     BlockFreelist用来管理空闲的Metablock,其定义在同目录下的metaspace.cpp中。所有空闲的Metablock都被添加到支持按照空闲空间大小排序和查找的二叉树BlockTreeDictionary中,BlockTreeDictionary实际是模板类BinaryTreeDictionary的别名,如下图:

BlockFreelist定义的属性比较简单,如下:

其中_dictionary会在第一次调用return_block归还空闲Metablock时初始化,WasteMultiplier是调用get_block获取满足指定大小的空闲Metablock时使用,要求查找到的空闲Metablock的大小不能超过目标大小的WasteMultiplier倍。这两方法就是BlockFreelist的关键,其实现如下:

void BlockFreelist::return_block(MetaWord* p, size_t word_size) {
  //根据内存块的起始地址和大小构造一个新的Metablock
  Metablock* free_chunk = ::new (p) Metablock(word_size);
  if (dictionary() == NULL) {
    //初始化_dictionary
   _dictionary = new BlockTreeDictionary();
  }
  //添加到二叉树中保存
  dictionary()->return_chunk(free_chunk);
}

BlockTreeDictionary* dictionary() const { return _dictionary; }

MetaWord* BlockFreelist::get_block(size_t word_size) {
  //_dictionary未初始化,肯定没有空闲的
  if (dictionary() == NULL) {
    return NULL;
  }
  //TreeChunk是一个模板类,BlockTreeDictionary的实现会用到
  //如果word_size太小则返回NULL
  if (word_size < TreeChunk<Metablock, FreeList<Metablock> >::min_size()) {
    // Dark matter.  Too small for dictionary.
    return NULL;
  }

  //查找大于等于目标大小的空闲Metablock
  Metablock* free_block =
    dictionary()->get_chunk(word_size, FreeBlockDictionary<Metablock>::atLeast);
  if (free_block == NULL) {
    return NULL;
  }

  const size_t block_size = free_block->size();
  //如果找到的空闲Metablock的大小大于目标大小的4倍,则将其归还,返回NULL,避免浪费
  if (block_size > WasteMultiplier * word_size) {
    return_block((MetaWord*)free_block, block_size);
    return NULL;
  }
  //如果小于目标大小的4倍
  MetaWord* new_block = (MetaWord*)free_block;
  assert(block_size >= word_size, "Incorrect size of block from freelist");
  //计算多余的空间
  const size_t unused = block_size - word_size;
  if (unused >= TreeChunk<Metablock, FreeList<Metablock> >::min_size()) {
    //将多余的空间归还
    return_block(new_block + word_size, unused);
  }

  return new_block;
}

template <class Chunk_t, class FreeList_t>
size_t TreeChunk<Chunk_t, FreeList_t>::_min_tree_chunk_size = sizeof(TreeChunk<Chunk_t,  FreeList_t>)/HeapWordSize;

这两方法的调用链如下:

其调用方就SpaceManager。 

五、VirtualSpaceNode

1、定义

      VirtualSpaceNode是VirtualSpaceList的一个节点,用来表示一大段连续的内存空间,一个VirtualSpaceNode对应一个单独的ReservedSpace和VirtualSpace。其定义的属性如下:

其中rs和virtual_space负责维护这段连续的内存空间,_next属性表示链表上下一个VirtualSpaceNode,_top表示未被分配的内存起始地址,_container_count表示这个VirtualSpaceNode包含的非空闲Metachunk的个数,获取Metachunk时_container_count增加,归还Metachunk到ChunkManager时减少,_reserved表示保留的未向操作系统申请内存的一块区域,MemRegion的定义如下:

 VirtualSpaceNode定义的方法大部分是获取这段连续内存空闲的属性的相关方法,如bottom,end,reserved_words等,实际是对VirtualSpace方法的包装,重点关注以下方法的实现。

2、构造方法和initialize

     构造方法有两个版本,负责初始化属性rs,与之对应的析构函数负责释放rs对应的内存地址空间。initialize是在构造方法执行完后根据已经初始化好的rs来初始化后virtual_space,初始化VirtualSpace的相关属性的,其源码实现如下:

VirtualSpaceNode::VirtualSpaceNode(size_t bytes) : _top(NULL), _next(NULL), _rs(), _container_count(0) {
  assert_is_size_aligned(bytes, Metaspace::reserve_alignment());

#if INCLUDE_CDS
  //DumpSharedSpaces表示将加载的类Dump到一个文件中给其他的JVM使用,默认为false,如果为true则申请一段连续的内存时需要
  //从Java堆空间的顶部申请,避免地址冲突
  if (DumpSharedSpaces) {
    bool large_pages = false; // No large pages when dumping the CDS archive.
    //SharedBaseAddress表示共享内存区域的基地址,64位下是32G,即从32G往后尝试申请一段连续的内存空间
    char* shared_base = (char*)align_ptr_up((char*)SharedBaseAddress, Metaspace::reserve_alignment());

    _rs = ReservedSpace(bytes, Metaspace::reserve_alignment(), large_pages, shared_base, 0);
    if (_rs.is_reserved()) {
      //分配成功
      assert(shared_base == 0 || _rs.base() == shared_base, "should match");
    } else {
      //在SharedBaseAddress上分配失败,则重试,不指定起始分配地址
      _rs = ReservedSpace(bytes, Metaspace::reserve_alignment(), large_pages);
    }
    MetaspaceShared::set_shared_rs(&_rs);
  } else
#endif
  {
    //判断是否使用内存页
    bool large_pages = should_commit_large_pages_when_reserving(bytes);
    _rs = ReservedSpace(bytes, Metaspace::reserve_alignment(), large_pages);
  }
  
  //如果申请成功
  if (_rs.is_reserved()) {
     //校验分配的地址空间是否符合要求
    assert(_rs.base() != NULL, "Catch if we get a NULL address");
    assert(_rs.size() != 0, "Catch if we get a 0 size");
    assert_is_ptr_aligned(_rs.base(), Metaspace::reserve_alignment());
    assert_is_size_aligned(_rs.size(), Metaspace::reserve_alignment());
    //记录日志
    MemTracker::record_virtual_memory_type((address)_rs.base(), mtClass);
  }
}

// Decide if large pages should be committed when the memory is reserved.
static bool should_commit_large_pages_when_reserving(size_t bytes) {
  if (UseLargePages && UseLargePagesInMetaspace && !os::can_commit_large_page_memory()) {
    size_t words = bytes / BytesPerWord;
    bool is_class = false; // We never reserve large pages for the class space.
    //如果当前Metaspace的剩余容量允许扩展指定大小
    if (MetaspaceGC::can_expand(words, is_class) &&
        MetaspaceGC::allowed_expansion() >= words) {
      return true;
    }
  }

  return false;
}

VirtualSpaceNode(ReservedSpace rs) : _top(NULL), _next(NULL), _rs(rs), _container_count(0) {}

VirtualSpaceNode::~VirtualSpaceNode() {
  _rs.release();
}

bool VirtualSpaceNode::initialize() {
  //_rs申请内存地址空间失败,返回false
  if (!_rs.is_reserved()) {
    return false;
  }

  //校验申请的地址空间是否合法
  assert_is_ptr_aligned(_rs.base(), Metaspace::commit_alignment());
  assert_is_size_aligned(_rs.size(), Metaspace::commit_alignment());

  //如果rs支持pre-committed,则设置pre_committed_size为rs的大小
  size_t pre_committed_size = _rs.special() ? _rs.size() : 0;
  //初始化virtual_space
  bool result = virtual_space()->initialize_with_granularity(_rs, pre_committed_size,
                                            Metaspace::commit_alignment());
  //申请内存成功
  if (result) {
    assert(virtual_space()->committed_size() == virtual_space()->actual_committed_size(),
        "Checking that the pre-committed memory was registered by the VirtualSpace");
    //设置其他属性
    set_top((MetaWord*)virtual_space()->low());
    set_reserved(MemRegion((HeapWord*)_rs.base(),
                 (HeapWord*)(_rs.base() + _rs.size())));

    assert(reserved()->start() == (HeapWord*) _rs.base(),
      err_msg("Reserved start was not set properly " PTR_FORMAT
        " != " PTR_FORMAT, reserved()->start(), _rs.base()));
    assert(reserved()->word_size() == _rs.size() / BytesPerWord,
      err_msg("Reserved size was not set properly " SIZE_FORMAT
        " != " SIZE_FORMAT, reserved()->word_size(),
        _rs.size() / BytesPerWord));
  }

  return result;
}

3、get_chunk_vs

      get_chunk_vs负责从virtual_space中分配一段指定大小的内存空间,其调用链如下:

其源码如下:

Metachunk* VirtualSpaceNode::get_chunk_vs(size_t chunk_word_size) {
  //校验已获取锁
  assert_lock_strong(SpaceManager::expand_lock());
  //从commited区域的内存分配一个Metachunk
  Metachunk* result = take_from_committed(chunk_word_size);
  if (result != NULL) {
    //分配成功,增加计数器
    inc_container_count();
  }
  return result;
}

Metachunk* VirtualSpaceNode::take_from_committed(size_t chunk_word_size) {
  //获取未分配内存的起始地址
  MetaWord* chunk_limit = top();
  assert(chunk_limit != NULL, "Not safe to call this method");

  //校验_virtual_space是否按照期望的方式expand,如果为false,则下面的is_available可能返回错误的结果
  assert(_virtual_space.committed_size() == _virtual_space.actual_committed_size(),
      "The committed memory doesn't match the expanded memory.");
  //如果剩余空间不足,返回NULL
  if (!is_available(chunk_word_size)) {
    //打印日志
    if (TraceMetadataChunkAllocation) {
      gclog_or_tty->print("VirtualSpaceNode::take_from_committed() not available %d words ", chunk_word_size);
      // Dump some information about the virtual space that is nearly full
      print_on(gclog_or_tty);
    }
    return NULL;
  }
  //将top指针往高地址移动
  inc_top(chunk_word_size);
  //初始化Metachunk
  Metachunk* result = ::new (chunk_limit) Metachunk(chunk_word_size, this);
  return result;
}

bool is_available(size_t word_size) { return word_size <= pointer_delta(end(), _top, sizeof(MetaWord)); }

MetaWord* end() const { return (MetaWord*) _virtual_space.high(); }

void inc_top(size_t word_size) { _top += word_size; }

void VirtualSpaceNode::inc_container_count() {
  assert_lock_strong(SpaceManager::expand_lock());
  //增加计数器
  _container_count++;
  assert(_container_count == container_count_slow(),
         err_msg("Inconsistency in countainer_count _container_count " SIZE_FORMAT
                 " container_count_slow() " SIZE_FORMAT,
                 _container_count, container_count_slow()));
}

uint VirtualSpaceNode::container_count_slow() {
  uint count = 0;
  Metachunk* chunk = first_chunk();
  Metachunk* invalid_chunk = (Metachunk*) top();
  //根据内存地址遍历所有的Metachunk,统计Metachunk的个数
  while (chunk < invalid_chunk ) {
    MetaWord* next = ((MetaWord*)chunk) + chunk->word_size();
    if (!chunk->is_tagged_free()) {
      count++;
    }
    chunk = (Metachunk*) next;
  }
  return count;
}

Metachunk* first_chunk() { return (Metachunk*) bottom(); }

MetaWord* bottom() const { return (MetaWord*) _virtual_space.low(); }

4、purge / retire

    purge方法用于将此VirtualSpaceNode保存的所有的Metachunk从ChunkManager管理的Metachunk freeList中移除,删除此VirtualSpaceNode时调用。retire方法是当前VirtualSpaceNode的剩余空间不足需要申请一个新的VirtualSpaceNode是调用的,retire方法会在当前VirtualSpaceNode的剩余空间内申请新的Metachunk,并将其添加到ChunkManager中,避免空间浪费。两方法的调用链如下:

其源码实现如下:

void VirtualSpaceNode::purge(ChunkManager* chunk_manager) {
  Metachunk* chunk = first_chunk();
  Metachunk* invalid_chunk = (Metachunk*) top();
  //按照起始内存地址遍历所有的Chunk,因为他们是地址连续的
  while (chunk < invalid_chunk ) {
    assert(chunk->is_tagged_free(), "Should be tagged free");
    MetaWord* next = ((MetaWord*)chunk) + chunk->word_size();
    //移除目标Metachunk
    chunk_manager->remove_chunk(chunk);
    //校验移除是否正常完成
    assert(chunk->next() == NULL &&
           chunk->prev() == NULL,
           "Was not removed from its list");
    chunk = (Metachunk*) next;
  }
}

void VirtualSpaceNode::retire(ChunkManager* chunk_manager) {
  for (int i = (int)MediumIndex; i >= (int)ZeroIndex; --i) {
    ChunkIndex index = (ChunkIndex)i;
    //获取不同规格的Metachunk的大小
    size_t chunk_size = chunk_manager->free_chunks(index)->size();
    //如果剩余空间充足
    while (free_words_in_vs() >= chunk_size) {
      DEBUG_ONLY(verify_container_count();)
      //申请一个新的Metachunk
      Metachunk* chunk = get_chunk_vs(chunk_size);
      assert(chunk != NULL, "allocation should have been successful");
      //申请成功将其交给ChunkManager
      chunk_manager->return_chunks(index, chunk);
      chunk_manager->inc_free_chunks_total(chunk_size);
      DEBUG_ONLY(verify_container_count();)
    }
  }
  assert(free_words_in_vs() == 0, "should be empty now");
}

size_t VirtualSpaceNode::free_words_in_vs() const {
  return pointer_delta(end(), top(), sizeof(MetaWord));
}
发布了117 篇原创文章 · 获赞 8 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_31865983/article/details/103542169
今日推荐