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

    目录

一、Metaspace

1、定义

2、ergo_initialize / global_initialize / post_initialize

3、构造和析构函数

4、allocate / deallocate

5、expand_and_allocate

 6、purge

二、总结

1、内存结构图

2、类数据结构


 在《Hotspot 内存管理之Metaspace(二) 源码解析》中讲解了负责Metaspace内存管理的VirtualSpaceList,ChunkManager,SpaceManager的实现,本篇博客重点讲解最外层的Metaspace的相关接口的实现。

一、Metaspace

1、定义

    Metaspace的定义位于hotspot src/share/vm/memory/metaspace.hpp中,Metaspace表示用来给Klass等元数据分配内存的一个内存空间,通常称为元空间,每个ClassLoader实例包括启动类加载器都会创建一个对应的Metaspace实例,每个Metaspace实例都有一个SpaceManager实例,通过SpaceManager完成内存分配与管理。Metaspace定义的属性如下:

  • static size_t _compressed_class_space_size;  //compressed class对应的Metaspace大小
  • static size_t _first_chunk_word_size;  //第一个NonClassType类型的MetaChunk的大小
  • static size_t _first_class_chunk_word_size; //第一个ClassType类型的MetaChunk的大小
  •  static size_t _commit_alignment;  //commit内存的粒度
  •  static size_t _reserve_alignment;  //reserve内存的粒度
  •  SpaceManager* _vsm;  //NonClassType类型的元数据对应的SpaceManager
  •  SpaceManager* _class_vsm; //ClassType类型的元数据对应的SpaceManager
  • static VirtualSpaceList* _space_list;  // NonClassType类型的元数据对应的VirtualSpaceList
  • static VirtualSpaceList* _class_space_list; // ClassType类型的元数据对应的VirtualSpaceList
  • static ChunkManager* _chunk_manager_metadata;   //NonClassType类型的元数据对应的ChunkManager
  • static ChunkManager* _chunk_manager_class;  // ClassType类型的元数据对应的ChunkManager
  • static const MetaspaceTracer* _tracer; //打印日志使用
  • AllocRecord * _alloc_record_head;  //AllocRecord链表的头部元素
  • AllocRecord * _alloc_record_tail; //AllocRecord链表的尾部元素

注意上述 ClassType类型的ChunkManager和VirtualSpaceList具体是指开启UseCompressedClassPointers下用来存储Class等元数据的元空间。

 AllocRecord类的定义如下:

就是一个简单的记录内存分配结果的数据结构。重点关注以下方法的实现。 

2、ergo_initialize / global_initialize / post_initialize

      ergo_initialize、global_initialize、post_initialize这三个方法都是Metaspace的初始化方法,ergo_initialize用于初始化Metaspace的各种参数,如MetaspaceSize,MaxMetaspaceSize,MinMetaspaceExpansion等;global_initialize方法用于初始化_first_chunk_word_size,_space_list,_chunk_manager_metadata等静态属性;post_initialize就调用MetaspaceGC::post_initialize方法。各方法的调用链如下:

三个的源码实现如下,其中DumpSharedSpaces表示将共享的Metaspace空间dump到一个文件中,给其他JVM使用,默认为false;UseCompressedOops和UseCompressedClassPointers表示使用压缩的oop指针和Klass指针,64位下默认为true;UseSharedSpaces表示使用基于文件的共享Metaspace,即不同的JVM进程通过将Metaspace映射到同一个文件实现Metaspace共享,默认为false。

void Metaspace::ergo_initialize() {
  if (DumpSharedSpaces) {
    // Using large pages when dumping the shared archive is currently not implemented.
    FLAG_SET_ERGO(bool, UseLargePagesInMetaspace, false);
  }

  size_t page_size = os::vm_page_size();
  if (UseLargePages && UseLargePagesInMetaspace) {
    page_size = os::large_page_size();
  }

  //初始化参数
  _commit_alignment  = page_size;
  _reserve_alignment = MAX2(page_size, (size_t)os::vm_allocation_granularity());

  MaxMetaspaceSize = align_size_down_bounded(MaxMetaspaceSize, _reserve_alignment);

  if (MetaspaceSize > MaxMetaspaceSize) {
    MetaspaceSize = MaxMetaspaceSize;
  }

  MetaspaceSize = align_size_down_bounded(MetaspaceSize, _commit_alignment);

  assert(MetaspaceSize <= MaxMetaspaceSize, "MetaspaceSize should be limited by MaxMetaspaceSize");

  if (MetaspaceSize < 256*K) {
    vm_exit_during_initialization("Too small initial Metaspace size");
  }

  MinMetaspaceExpansion = align_size_down_bounded(MinMetaspaceExpansion, _commit_alignment);
  MaxMetaspaceExpansion = align_size_down_bounded(MaxMetaspaceExpansion, _commit_alignment);

  CompressedClassSpaceSize = align_size_down_bounded(CompressedClassSpaceSize, _reserve_alignment);
  set_compressed_class_space_size(CompressedClassSpaceSize);

  //VIRTUALSPACEMULTIPLIER的值是2
  uintx min_metaspace_sz =
      VIRTUALSPACEMULTIPLIER * InitialBootClassLoaderMetaspaceSize;
  if (UseCompressedClassPointers) {
    if ((min_metaspace_sz + CompressedClassSpaceSize) >  MaxMetaspaceSize) {
      if (min_metaspace_sz >= MaxMetaspaceSize) {
        vm_exit_during_initialization("MaxMetaspaceSize is too small.");
      } else {
        FLAG_SET_ERGO(uintx, CompressedClassSpaceSize,
                      MaxMetaspaceSize - min_metaspace_sz);
      }
    }
  } else if (min_metaspace_sz >= MaxMetaspaceSize) {
    FLAG_SET_ERGO(uintx, InitialBootClassLoaderMetaspaceSize,
                  min_metaspace_sz);
  }

}

 static void set_compressed_class_space_size(size_t size) {
    _compressed_class_space_size = size;
  }


void Metaspace::global_initialize() {
  MetaspaceGC::initialize();

  // Initialize the alignment for shared spaces.
  int max_alignment = os::vm_allocation_granularity();
  size_t cds_total = 0;

  MetaspaceShared::set_max_alignment(max_alignment);
  //DumpSharedSpaces默认为false
  if (DumpSharedSpaces) {
#if INCLUDE_CDS
    MetaspaceShared::estimate_regions_size();

    SharedReadOnlySize  = align_size_up(SharedReadOnlySize,  max_alignment);
    SharedReadWriteSize = align_size_up(SharedReadWriteSize, max_alignment);
    SharedMiscDataSize  = align_size_up(SharedMiscDataSize,  max_alignment);
    SharedMiscCodeSize  = align_size_up(SharedMiscCodeSize,  max_alignment);

    // the min_misc_code_size estimate is based on MetaspaceShared::generate_vtable_methods()
    uintx min_misc_code_size = align_size_up(
      (MetaspaceShared::num_virtuals * MetaspaceShared::vtbl_list_size) *
        (sizeof(void*) + MetaspaceShared::vtbl_method_size) + MetaspaceShared::vtbl_common_code_size,
          max_alignment);

    if (SharedMiscCodeSize < min_misc_code_size) {
      report_out_of_shared_space(SharedMiscCode);
    }

    cds_total = FileMapInfo::shared_spaces_size();
    cds_total = align_size_up(cds_total, _reserve_alignment);
    _space_list = new VirtualSpaceList(cds_total/wordSize);
    _chunk_manager_metadata = new ChunkManager(SpecializedChunk, SmallChunk, MediumChunk);

    if (!_space_list->initialization_succeeded()) {
      vm_exit_during_initialization("Unable to dump shared archive.", NULL);
    }

#ifdef _LP64
    if (cds_total + compressed_class_space_size() > UnscaledClassSpaceMax) {
      vm_exit_during_initialization("Unable to dump shared archive.",
          err_msg("Size of archive (" SIZE_FORMAT ") + compressed class space ("
                  SIZE_FORMAT ") == total (" SIZE_FORMAT ") is larger than compressed "
                  "klass limit: " SIZE_FORMAT, cds_total, compressed_class_space_size(),
                  cds_total + compressed_class_space_size(), UnscaledClassSpaceMax));
    }

    assert(UseCompressedOops && UseCompressedClassPointers,
      "UseCompressedOops and UseCompressedClassPointers must be set");
    Universe::set_narrow_klass_base((address)_space_list->current_virtual_space()->bottom());
    if (TraceMetavirtualspaceAllocation && Verbose) {
      gclog_or_tty->print_cr("Setting_narrow_klass_base to Address: " PTR_FORMAT,
                             _space_list->current_virtual_space()->bottom());
    }

    Universe::set_narrow_klass_shift(0);
#endif // _LP64
#endif // INCLUDE_CDS
  } else {
#if INCLUDE_CDS
    // If using shared space, open the file that contains the shared space
    // and map in the memory before initializing the rest of metaspace (so
    // the addresses don't conflict)
    address cds_address = NULL;
    //UseSharedSpaces默认是false
    if (UseSharedSpaces) {
      FileMapInfo* mapinfo = new FileMapInfo();
      if (mapinfo->initialize() && MetaspaceShared::map_shared_spaces(mapinfo)) {
        cds_total = FileMapInfo::shared_spaces_size();
        cds_address = (address)mapinfo->region_base(0);
      } else {
        assert(!mapinfo->is_open() && !UseSharedSpaces,
               "archive file not closed or shared spaces not disabled.");
      }
    }
#endif // INCLUDE_CDS
#ifdef _LP64
    //UseCompressedClassPointers为false,DumpSharedSpaces为true时,返回true,64位下默认返回true
    if (using_class_space()) {
      if (UseSharedSpaces) {
#if INCLUDE_CDS
        char* cds_end = (char*)(cds_address + cds_total);
        cds_end = (char *)align_ptr_up(cds_end, _reserve_alignment);
        allocate_metaspace_compressed_klass_ptrs(cds_end, cds_address);
#endif
      } else {
        //以堆内存的终止地址作为起始地址申请内存,避免堆内存与Metaspace的内存地址冲突
        char* base = (char*)align_ptr_up(Universe::heap()->reserved_region().end(), _reserve_alignment);
        allocate_metaspace_compressed_klass_ptrs(base, 0);
      }
    }
#endif // _LP64

    //计算_first_chunk_word_size和_first_class_chunk_word_size
    _first_chunk_word_size = InitialBootClassLoaderMetaspaceSize / BytesPerWord;
    _first_chunk_word_size = align_word_size_up(_first_chunk_word_size);
    _first_class_chunk_word_size = MIN2((size_t)MediumChunk*6,
                                       (CompressedClassSpaceSize/BytesPerWord)*2);
    _first_class_chunk_word_size = align_word_size_up(_first_class_chunk_word_size);

    //VIRTUALSPACEMULTIPLIER的取值是2,初始化_space_list和_chunk_manager_metadata
    size_t word_size = VIRTUALSPACEMULTIPLIER * _first_chunk_word_size;
    word_size = align_size_up(word_size, Metaspace::reserve_alignment_words());
    _space_list = new VirtualSpaceList(word_size);
    _chunk_manager_metadata = new ChunkManager(SpecializedChunk, SmallChunk, MediumChunk);
    
    //_space_list初始化失败
    if (!_space_list->initialization_succeeded()) {
      vm_exit_during_initialization("Unable to setup metadata virtual space list.", NULL);
    }
  }

  _tracer = new MetaspaceTracer();
}

 // Return TRUE only if UseCompressedClassPointers is True and DumpSharedSpaces is False.
  static bool using_class_space() {
    return NOT_LP64(false) LP64_ONLY(UseCompressedClassPointers && !DumpSharedSpaces);
  }


// Try to allocate the metaspace at the requested addr.
void Metaspace::allocate_metaspace_compressed_klass_ptrs(char* requested_addr, address cds_base) {
  //校验参数
  assert(using_class_space(), "called improperly");
  assert(UseCompressedClassPointers, "Only use with CompressedKlassPtrs");
  assert(compressed_class_space_size() < KlassEncodingMetaspaceMax,
         "Metaspace size is too big");
  assert_is_ptr_aligned(requested_addr, _reserve_alignment);
  assert_is_ptr_aligned(cds_base, _reserve_alignment);
  assert_is_size_aligned(compressed_class_space_size(), _reserve_alignment);

  //尝试在指定起始地址处申请一段连续的内存空间
  bool large_pages = false;
  ReservedSpace metaspace_rs = ReservedSpace(compressed_class_space_size(),
                                             _reserve_alignment,
                                             large_pages,
                                             requested_addr, 0);
  if (!metaspace_rs.is_reserved()) {
#if INCLUDE_CDS
    if (UseSharedSpaces) {
      size_t increment = align_size_up(1*G, _reserve_alignment);
      char *addr = requested_addr;
      while (!metaspace_rs.is_reserved() && (addr + increment > addr) &&
             can_use_cds_with_metaspace_addr(addr + increment, cds_base)) {
        addr = addr + increment;
        metaspace_rs = ReservedSpace(compressed_class_space_size(),
                                     _reserve_alignment, large_pages, addr, 0);
      }
    }
#endif
    // 忽略起始地址,尝试重新申请,分配失败抛出异常
    if (!metaspace_rs.is_reserved()) {
      metaspace_rs = ReservedSpace(compressed_class_space_size(),
                                   _reserve_alignment, large_pages);
      if (!metaspace_rs.is_reserved()) {
        vm_exit_during_initialization(err_msg("Could not allocate metaspace: %d bytes",
                                              compressed_class_space_size()));
      }
    }
  }

  // If we got here then the metaspace got allocated.
  MemTracker::record_virtual_memory_type((address)metaspace_rs.base(), mtClass);

#if INCLUDE_CDS
  if (UseSharedSpaces && !can_use_cds_with_metaspace_addr(metaspace_rs.base(), cds_base)) {
    FileMapInfo::stop_sharing_and_unmap(
        "Could not allocate metaspace at a compatible address");
  }
#endif
  //重置narrow_klass_base和narrow_klass_shift
  set_narrow_klass_base_and_shift((address)metaspace_rs.base(),
                                  UseSharedSpaces ? (address)cds_base : 0);
  initialize_class_space(metaspace_rs);
  
  //打印日志
  if (PrintCompressedOopsMode || (PrintMiscellaneous && Verbose)) {
      print_compressed_class_space(gclog_or_tty, requested_addr);
  }
}

void Metaspace::set_narrow_klass_base_and_shift(address metaspace_base, address cds_base) {
  address lower_base;
  address higher_address;
#if INCLUDE_CDS
  if (UseSharedSpaces) {
    higher_address = MAX2((address)(cds_base + FileMapInfo::shared_spaces_size()),
                          (address)(metaspace_base + compressed_class_space_size()));
    lower_base = MIN2(metaspace_base, cds_base);
  } else
#endif
  {
    //lower_base是起始地址,higher_address是终止地址
    higher_address = metaspace_base + compressed_class_space_size();
    lower_base = metaspace_base;

    uint64_t klass_encoding_max = UnscaledClassSpaceMax << LogKlassAlignmentInBytes;
    // If compressed class space fits in lower 32G, we don't need a base.
    if (higher_address <= (address)klass_encoding_max) {
      lower_base = 0; // effectively lower base is zero.
    }
  }

  Universe::set_narrow_klass_base(lower_base);

  if ((uint64_t)(higher_address - lower_base) <= UnscaledClassSpaceMax) {
    Universe::set_narrow_klass_shift(0);
  } else {
    assert(!UseSharedSpaces, "Cannot shift with UseSharedSpaces");
    Universe::set_narrow_klass_shift(LogKlassAlignmentInBytes);
  }
}

void Metaspace::initialize_class_space(ReservedSpace rs) {
  // 初始化_class_space_list和_chunk_manager_class
  assert(rs.size() >= CompressedClassSpaceSize,
         err_msg(SIZE_FORMAT " != " UINTX_FORMAT, rs.size(), CompressedClassSpaceSize));
  assert(using_class_space(), "Must be using class space");
  _class_space_list = new VirtualSpaceList(rs);
  _chunk_manager_class = new ChunkManager(ClassSpecializedChunk, ClassSmallChunk, ClassMediumChunk);

  if (!_class_space_list->initialization_succeeded()) {
    vm_exit_during_initialization("Failed to setup compressed class space virtual space list.");
  }
}

void Metaspace::post_initialize() {
  MetaspaceGC::post_initialize();
}

3、构造和析构函数

     构造函数用于初始化_vsm和_class_vsm,分配第一个Chunk;析构函数用于释放_vsm和_class_vsm。两者的调用链如下:

上述构造方法的调用方ClassLoaderData::initialize_shared_metaspaces()是初始化启动类加载器使用的Metaspace,ClassLoaderData::metaspace_non_null是初始化其他的非启动类加载器使用的Metaspace,属于惰性初始化。

析构方法是在ClassLoaderData被删除时调用的。两者的源码说明如下: 

Metaspace::Metaspace(Mutex* lock, MetaspaceType type) {
  initialize(lock, type);
}

Metaspace::~Metaspace() {
  //释放SpaceManager
  delete _vsm;
  if (using_class_space()) {
    delete _class_vsm;
  }
}

void Metaspace::initialize(Mutex* lock, MetaspaceType type) {
  //校验space_list等初始化完成
  verify_global_initialization();

  //初始化_vsm和_class_vsm
  _vsm = new SpaceManager(NonClassType, lock);
  if (using_class_space()) {
    _class_vsm = new SpaceManager(ClassType, lock);
  }
  
  //获取锁
  MutexLockerEx cl(SpaceManager::expand_lock(), Mutex::_no_safepoint_check_flag);

  //初始化第一个Chunk
  initialize_first_chunk(type, NonClassType);
  if (using_class_space()) {
    initialize_first_chunk(type, ClassType);
  }

  _alloc_record_head = NULL;
  _alloc_record_tail = NULL;
}

void Metaspace::verify_global_initialization() {
  assert(space_list() != NULL, "Metadata VirtualSpaceList has not been initialized");
  assert(chunk_manager_metadata() != NULL, "Metadata ChunkManager has not been initialized");

  if (using_class_space()) {
    assert(class_space_list() != NULL, "Class VirtualSpaceList has not been initialized");
    assert(chunk_manager_class() != NULL, "Class ChunkManager has not been initialized");
  }
}

void Metaspace::initialize_first_chunk(MetaspaceType type, MetadataType mdtype) {
  Metachunk* chunk = get_initialization_chunk(type, mdtype);
  if (chunk != NULL) {
    //chunk分配成功,将其添加到SpaceManager中,将其作为当前使用的Chunk
    get_space_manager(mdtype)->add_chunk(chunk, true);
  }
}

Metachunk* Metaspace::get_initialization_chunk(MetaspaceType type, MetadataType mdtype) {
  //获取初始Chunk的大小
  size_t chunk_word_size = get_space_manager(mdtype)->get_initial_chunk_size(type);

  //从ChunkManager管理的空闲Chunk中分配一个满足大小的chunk
  Metachunk* chunk = get_chunk_manager(mdtype)->chunk_freelist_allocate(chunk_word_size);

  if (chunk == NULL) {
    //查找失败从VirtualSpaceList中分配一个新的Chunk
    chunk = get_space_list(mdtype)->get_new_chunk(chunk_word_size,
                                                  get_space_manager(mdtype)->medium_chunk_bunch());
  }

  if (DumpSharedSpaces && chunk == NULL) {
    //记录分配失败
    report_insufficient_metaspace(MetaspaceAux::committed_bytes() + chunk_word_size * BytesPerWord);
  }

  return chunk;
}

SpaceManager* get_space_manager(MetadataType mdtype) {
    assert(mdtype != MetadataTypeCount, "MetadaTypeCount can't be used as mdtype");
    return mdtype == ClassType ? class_vsm() : vsm();
  }

static ChunkManager* get_chunk_manager(MetadataType mdtype) {
    assert(mdtype != MetadataTypeCount, "MetadaTypeCount can't be used as mdtype");
    return mdtype == ClassType ? chunk_manager_class() : chunk_manager_metadata();
  }

 static VirtualSpaceList* get_space_list(MetadataType mdtype) {
    assert(mdtype != MetadataTypeCount, "MetadaTypeCount can't be used as mdtype");
    return mdtype == ClassType ? class_space_list() : space_list();
  }

4、allocate / deallocate

     allocate方法用于从Metaspace分配内存,与之对应的有个deallocate方法释放内存,将内存作为MetaBlock放入SpaceManager的放到block_freelists中重复利用。因为Metaspace分配的都是元数据,一般不会被释放,除非对应的ClassLoader被垃圾回收掉了,所以该方法很少被调用,当对应的ClassLoader会垃圾回收掉了,对应的Metaspace的SpaceManager使用的MetaChunk会被整体归还到ChunkManager中重新分配给其他的Metaspace。allocate方法的调用链如下:

从调用方可知上述类实例都会在Metaspace中分配内存。其源码实现如下:

MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size,
                              bool read_only, MetaspaceObj::Type type, TRAPS) {
  if (HAS_PENDING_EXCEPTION) {
    //不能有未处理异常
    assert(false, "Should not allocate with exception pending");
    return NULL;  // caller does a CHECK_NULL too
  }
  //loader_data不能为空
  assert(loader_data != NULL, "Should never pass around a NULL loader_data. "
        "ClassLoaderData::the_null_class_loader_data() should have been used.");

  // DumpSharedSpaces默认为false
  if (DumpSharedSpaces) {
    assert(type > MetaspaceObj::UnknownType && type < MetaspaceObj::_number_of_types, "sanity");
    Metaspace* space = read_only ? loader_data->ro_metaspace() : loader_data->rw_metaspace();
    MetaWord* result = space->allocate(word_size, NonClassType);
    if (result == NULL) {
      report_out_of_shared_space(read_only ? SharedReadOnly : SharedReadWrite);
    }
    if (PrintSharedSpaces) {
      space->record_allocation(result, type, space->vsm()->get_raw_word_size(word_size));
    }

    // Zero initialize.
    Copy::fill_to_aligned_words((HeapWord*)result, word_size, 0);

    return result;
  }

  MetadataType mdtype = (type == MetaspaceObj::ClassType) ? ClassType : NonClassType;

  //获取ClassLoaderData的_metaspace,然后分配内存
  MetaWord* result = loader_data->metaspace_non_null()->allocate(word_size, mdtype);

  if (result == NULL) {
    //报告分配事变
    tracer()->report_metaspace_allocation_failure(loader_data, word_size, type, mdtype);
    if (is_init_completed()) {
      //启动完成通过GC释放部分内存,然后尝试重新分配
      result = Universe::heap()->collector_policy()->satisfy_failed_metadata_allocation(
          loader_data, word_size, mdtype);
    }
  }

  if (result == NULL) {
    //报告分配失败,会对外抛出异常
    report_metadata_oome(loader_data, word_size, type, mdtype, CHECK_NULL);
  }

  //将分配的内存初始化成0
  Copy::fill_to_aligned_words((HeapWord*)result, word_size, 0);

  return result;
}

MetaWord* Metaspace::allocate(size_t word_size, MetadataType mdtype) {
  //通过SpaceManager分配内存
  if (is_class_space_allocation(mdtype)) {
    return  class_vsm()->allocate(word_size);
  } else {
    return  vsm()->allocate(word_size);
  }
}

void Metaspace::deallocate(MetaWord* ptr, size_t word_size, bool is_class) {
  //如果在安全点
  if (SafepointSynchronize::is_at_safepoint()) {
     //DumpSharedSpaces默认为false
    if (DumpSharedSpaces && PrintSharedSpaces) {
      record_deallocation(ptr, vsm()->get_raw_word_size(word_size));
    }
    //校验必须是VM Thread
    assert(Thread::current()->is_VM_thread(), "should be the VM thread");
    //获取锁
    MutexLockerEx ml(vsm()->lock(), Mutex::_no_safepoint_check_flag);
    //如果word_size过小则不处理
    if (word_size < TreeChunk<Metablock, FreeList<Metablock> >::min_size()) {
      // Dark matter.  Too small for dictionary.
      return;
    }
    //通过不同的SpaceManager释放,变成MetaBlock放到block_freelists中重复利用
    if (is_class && using_class_space()) {
      class_vsm()->deallocate(ptr, word_size);
    } else {
      vsm()->deallocate(ptr, word_size);
    }
  } else {
    MutexLockerEx ml(vsm()->lock(), Mutex::_no_safepoint_check_flag);

    if (word_size < TreeChunk<Metablock, FreeList<Metablock> >::min_size()) {
      // Dark matter.  Too small for dictionary.
      return;
    }
    if (is_class && using_class_space()) {
      class_vsm()->deallocate(ptr, word_size);
    } else {
      vsm()->deallocate(ptr, word_size);
    }
  }
}

5、expand_and_allocate

     expand_and_allocate方法用于GC结束后尝试扩展Metaspace的空间并从扩展后的Metaspace分配内存,其调用方如下:

该方法的源码如下:

MetaWord* Metaspace::expand_and_allocate(size_t word_size, MetadataType mdtype) {
  //计算允许扩展的空间
  size_t delta_bytes = MetaspaceGC::delta_capacity_until_GC(word_size * BytesPerWord);
  assert(delta_bytes > 0, "Must be");

  size_t before = 0;
  size_t after = 0;
  bool can_retry = true;
  MetaWord* res;
  bool incremented;

  //扩展失败依然尝试分配内存,因为扩展失败可能是因为其他线程已经完成了扩展
  do {
    incremented = MetaspaceGC::inc_capacity_until_GC(delta_bytes, &after, &before, &can_retry);
    res = allocate(word_size, mdtype);
  } while (!incremented && res == NULL && can_retry);

  if (incremented) {
    //记录扩展成功
    tracer()->report_gc_threshold(before, after,
                                  MetaspaceGCThresholdUpdater::ExpandAndAllocate);
    if (PrintGCDetails && Verbose) {
      gclog_or_tty->print_cr("Increase capacity to GC from " SIZE_FORMAT
          " to " SIZE_FORMAT, before, after);
    }
  }

  return res;
}

MetaWord* Metaspace::allocate(size_t word_size, MetadataType mdtype) {
  if (is_class_space_allocation(mdtype)) {
    return  class_vsm()->allocate(word_size);
  } else {
    return  vsm()->allocate(word_size);
  }
}

 6、purge

      purge方法是Metaspace关联的ClassLoaderData因为垃圾回收或者被主动释放时用来释放Metaspace曾经使用过的因为SpaceManager被销毁导致空闲的VirtualSpaceNode,该方法的调用链如下:

该方法的源码如下:

void Metaspace::purge(MetadataType mdtype) {
  get_space_list(mdtype)->purge(get_chunk_manager(mdtype));
}

void Metaspace::purge() {
  MutexLockerEx cl(SpaceManager::expand_lock(),
                   Mutex::_no_safepoint_check_flag);
  purge(NonClassType);
  if (using_class_space()) {
    purge(ClassType);
  }
}

static VirtualSpaceList* get_space_list(MetadataType mdtype) {
    assert(mdtype != MetadataTypeCount, "MetadaTypeCount can't be used as mdtype");
    return mdtype == ClassType ? class_space_list() : space_list();
  }
  
static ChunkManager* get_chunk_manager(MetadataType mdtype) {
    assert(mdtype != MetadataTypeCount, "MetadaTypeCount can't be used as mdtype");
    return mdtype == ClassType ? chunk_manager_class() : chunk_manager_metadata();
  }

二、总结

1、内存结构图

每个VirtualSpaceNode都对应一个表示一段连续内存空间的ReservedSpace和VirtualSpace,VirtualSpaceNode负责分配满足大小的Metachunk。VirtualSpaceList首先从当前使用的VirtualSpaceNode即_current_virtual_space中分配,当其空间不足时,VirtualSpaceList会创建一个新的VirtualSpaceNode,将旧的VirtualSpaceNode的剩余空间分配成若干个标准大小的Metachunk,保证其空间不浪费,然后将其插入到VirtualSpaceList的_virtual_space_list链表中,将其作为新的VirtualSpaceNode的next节点,新的VirtualSpaceNode变成_current_virtual_space,然后从新节点中分配Metachunk。参考上一篇《Hotspot 内存管理之Metaspace(二) 源码解析》中VirtualSpaceList::get_new_chunk方法的实现。

创建Klass等需要从Metaspace中分配内存场景都是从Metachunk中分配,如果当前Metachunk内存空间不够了会申请一个新的MetaChunk,从新的MetaChunk中分配。当需要释放Klass等元数据占用的内存时,这些元数据对应的内存块会作为MetaBlock放到SpaceManager中的_block_freelists链表中被重复利用。参考上一篇《Hotspot 内存管理之Metaspace(二) 源码解析》中SpaceManager allocate和deallocat方法的实现。

2、类数据结构

Metaspace根据MetadataType分别建立了对应的静态的VirtualSpaceList和ChunkManager,这两个是全局的负责管理所有Metaspace实例的VirtualSpaceNode分配和空闲的Metachunk。每个Metaspace实例根据MetadataType分别有一个对应的SpaceManager,SpaceManager是内存分配和释放的总的入口,分配内存时首先从_block_freelists中分配,如果内存不足会尝试从_current_chunk中分配,如果分配失败会从尝试从对应类型的全局ChunkManager获取一个新的满足大小的Chunk,如果获取失败再从对应类型的全局VirtualSpaceList中获取一个新的Metachunk。获取新的Metachunk后,将其加入到合适的_chunks_in_use列表中,然后从新的Metachunk中分配内存。释放内存时则是将对应的内存块作为MetaBlock归还到_block_freelists中从而被重复利用。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值