目录
2、ergo_initialize / global_initialize / post_initialize
在《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中从而被重复利用。