目录
1、initialize / post_initialize
2、inc_capacity_until_GC / dec_capacity_until_GC
2、used_bytes / used_bytes_slow
在上一篇《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));
}