在 OpenJDK 17 中,klass
对象(即 Java 类的元数据)的内存分配通常在元空间(Metaspace)中进行。元空间是 Java 堆外的一块内存区域,用于存储类的元数据。以下几个关键函数,它们共同参与了 klass
对象的内存分配过程:
-
ConstantPool::allocate_resolved_klasses
:- 这个函数用于为
ConstantPool
中解析后的类条目分配内存。它创建了一个Array<Klass*>
类型的数组,用于存储指向已解析类的指针。
- 这个函数用于为
-
MetadataFactory::new_array
:- 这是一个模板函数,用于创建一个新的元数据数组。它调用
Array<T>
类的构造函数来分配内存,并返回一个指向新数组的指针。
- 这是一个模板函数,用于创建一个新的元数据数组。它调用
-
Array<T>::operator new
:- 这个模板函数是
new
操作符的重载版本,专门为元数据数组分配内存。它调用Metaspace::allocate
来在元空间中分配内存。
- 这个模板函数是
-
Metaspace::allocate
:- 这个函数是元空间内存分配的核心函数。它接受类加载器数据、请求的字大小、类型和线程指针作为参数,并返回一个指向分配的内存区域的指针。
-
ClassLoaderMetaspace::allocate
:- 这个函数用于从类加载器的元空间中分配内存。它根据元数据类型(类或非类)选择正确的内存区域(类空间或非类空间)进行分配。
-
MetaspaceArena::allocate
:- 这个函数尝试从当前的
MetaspaceArena
中分配内存。如果当前块太小或不足以容纳请求的大小,它会尝试扩展当前块或分配一个新的块。
- 这个函数尝试从当前的
-
Metachunk::allocate
:- 这个函数用于在
Metachunk
中分配内存。Metachunk
是元空间中用于存储元数据的一块内存区域。
- 这个函数用于在
这些函数共同构成了 OpenJDK 中元空间分配的框架,它们确保了类的元数据能够在堆外内存中被有效地管理和回收。通过这种方式,OpenJDK 减少了 Full GC 的频率,提高了 JVM 的性能和稳定性。
##C++源代码
void ConstantPool::allocate_resolved_klasses(ClassLoaderData* loader_data, int num_klasses, TRAPS) {
// A ConstantPool can't possibly have 0xffff valid class entries,
// because entry #0 must be CONSTANT_Invalid, and each class entry must refer to a UTF8
// entry for the class's name. So at most we will have 0xfffe class entries.
// This allows us to use 0xffff (ConstantPool::_temp_resolved_klass_index) to indicate
// UnresolvedKlass entries that are temporarily created during class redefinition.
assert(num_klasses < CPKlassSlot::_temp_resolved_klass_index, "sanity");
assert(resolved_klasses() == NULL, "sanity");
Array<Klass*>* rk = MetadataFactory::new_array<Klass*>(loader_data, num_klasses, CHECK);
set_resolved_klasses(rk);
}
template <typename T>
static Array<T>* new_array(ClassLoaderData* loader_data, int length, TRAPS) {
// The "true" argument is because all metadata arrays are read only when
// dumped to the shared archive
return new (loader_data, length, THREAD) Array<T>(length);
}
template <typename T>
inline void* Array<T>::operator new(size_t size, ClassLoaderData* loader_data, int length, TRAPS) throw() {
size_t word_size = Array::size(length);
return (void*) Metaspace::allocate(loader_data, word_size,
MetaspaceObj::array_type(sizeof(T)), THREAD);
}
MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size,
MetaspaceObj::Type type, TRAPS) {
if (HAS_PENDING_EXCEPTION) {
assert(false, "Should not allocate with exception pending");
return NULL; // caller does a CHECK_NULL too
}
MetaWord* result = allocate(loader_data, word_size, type);
if (result == NULL) {
MetadataType mdtype = (type == MetaspaceObj::ClassType) ? ClassType : NonClassType;
tracer()->report_metaspace_allocation_failure(loader_data, word_size, type, mdtype);
// Allocation failed.
if (is_init_completed()) {
// Only start a GC if the bootstrapping has completed.
// Try to clean out some heap memory and retry. This can prevent premature
// expansion of the metaspace.
result = Universe::heap()->satisfy_failed_metadata_allocation(loader_data, word_size, mdtype);
}
if (result == NULL) {
report_metadata_oome(loader_data, word_size, type, mdtype, THREAD);
assert(HAS_PENDING_EXCEPTION, "sanity");
return NULL;
}
// Zero initialize.
Copy::fill_to_words((HeapWord*)result, word_size, 0);
log_trace(metaspace)("Metaspace::allocate: type %d return " PTR_FORMAT ".", (int)type, p2i(result));
}
return result;
}
MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size,
MetaspaceObj::Type type) {
assert(word_size <= Metaspace::max_allocation_word_size(),
"allocation size too large (" SIZE_FORMAT ")", word_size);
assert(loader_data != NULL, "Should never pass around a NULL loader_data. "
"ClassLoaderData::the_null_class_loader_data() should have been used.");
MetadataType mdtype = (type == MetaspaceObj::ClassType) ? ClassType : NonClassType;
// Try to allocate metadata.
MetaWord* result = loader_data->metaspace_non_null()->allocate(word_size, mdtype);
if (result != NULL) {
// Zero initialize.
Copy::fill_to_words((HeapWord*)result, word_size, 0);
log_trace(metaspace)("Metaspace::allocate: type %d return " PTR_FORMAT ".", (int)type, p2i(result));
}
return result;
}
// Allocate word_size words from Metaspace.
MetaWord* ClassLoaderMetaspace::allocate(size_t word_size, Metaspace::MetadataType mdType) {
if (Metaspace::is_class_space_allocation(mdType)) {
return class_space_arena()->allocate(word_size);
} else {
return non_class_space_arena()->allocate(word_size);
}
}
MetaWord* MetaspaceArena::allocate(size_t requested_word_size) {
MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag);
UL2(trace, "requested " SIZE_FORMAT " words.", requested_word_size);
MetaWord* p = NULL;
const size_t raw_word_size = get_raw_word_size_for_requested_word_size(requested_word_size);
// 1) Attempt to allocate from the free blocks list
// (Note: to reduce complexity, deallocation handling is disabled if allocation guards
// are enabled, see Settings::ergo_initialize())
if (Settings::handle_deallocations() && _fbl != NULL && !_fbl->is_empty()) {
p = _fbl->remove_block(raw_word_size);
if (p != NULL) {
DEBUG_ONLY(InternalStats::inc_num_allocs_from_deallocated_blocks();)
UL2(trace, "taken from fbl (now: %d, " SIZE_FORMAT ").",
_fbl->count(), _fbl->total_size());
// Note: Space which is kept in the freeblock dictionary still counts as used as far
// as statistics go; therefore we skip the epilogue in this function to avoid double
// accounting.
return p;
}
}
bool current_chunk_too_small = false;
bool commit_failure = false;
if (current_chunk() != NULL) {
// 2) Attempt to satisfy the allocation from the current chunk.
// If the current chunk is too small to hold the requested size, attempt to enlarge it.
// If that fails, retire the chunk.
if (current_chunk()->free_words() < raw_word_size) {
if (!attempt_enlarge_current_chunk(raw_word_size)) {
current_chunk_too_small = true;
} else {
DEBUG_ONLY(InternalStats::inc_num_chunks_enlarged();)
UL(debug, "enlarged chunk.");
}
}
// Commit the chunk far enough to hold the requested word size. If that fails, we
// hit a limit (either GC threshold or MaxMetaspaceSize). In that case retire the
// chunk.
if (!current_chunk_too_small) {
if (!current_chunk()->ensure_committed_additional(raw_word_size)) {
UL2(info, "commit failure (requested size: " SIZE_FORMAT ")", raw_word_size);
commit_failure = true;
}
}
// Allocate from the current chunk. This should work now.
if (!current_chunk_too_small && !commit_failure) {
p = current_chunk()->allocate(raw_word_size);
assert(p != NULL, "Allocation from chunk failed.");
}
}
if (p == NULL) {
// If we are here, we either had no current chunk to begin with or it was deemed insufficient.
assert(current_chunk() == NULL ||
current_chunk_too_small || commit_failure, "Sanity");
Metachunk* new_chunk = allocate_new_chunk(raw_word_size);
if (new_chunk != NULL) {
UL2(debug, "allocated new chunk " METACHUNK_FORMAT " for requested word size " SIZE_FORMAT ".",
METACHUNK_FORMAT_ARGS(new_chunk), requested_word_size);
assert(new_chunk->free_below_committed_words() >= raw_word_size, "Sanity");
if (Settings::new_chunks_are_fully_committed()) {
assert(new_chunk->is_fully_committed(), "Chunk should be fully committed.");
}
// We have a new chunk. Before making it the current chunk, retire the old one.
if (current_chunk() != NULL) {
salvage_chunk(current_chunk());
DEBUG_ONLY(InternalStats::inc_num_chunks_retired();)
}
_chunks.add(new_chunk);
// Now, allocate from that chunk. That should work.
p = current_chunk()->allocate(raw_word_size);
assert(p != NULL, "Allocation from chunk failed.");
} else {
UL2(info, "failed to allocate new chunk for requested word size " SIZE_FORMAT ".", requested_word_size);
}
}
#ifdef ASSERT
// When using allocation guards, establish a prefix.
if (p != NULL && Settings::use_allocation_guard()) {
p = establish_prefix(p, raw_word_size);
}
#endif
if (p == NULL) {
InternalStats::inc_num_allocs_failed_limit();
} else {
DEBUG_ONLY(InternalStats::inc_num_allocs();)
_total_used_words_counter->increment_by(raw_word_size);
}
SOMETIMES(verify_locked();)
if (p == NULL) {
UL(info, "allocation failed, returned NULL.");
} else {
UL2(trace, "after allocation: %u chunk(s), current:" METACHUNK_FULL_FORMAT,
_chunks.count(), METACHUNK_FULL_FORMAT_ARGS(current_chunk()));
UL2(trace, "returning " PTR_FORMAT ".", p2i(p));
}
return p;
}
MetaWord* Metachunk::allocate(size_t request_word_size) {
// Caller must have made sure this works
assert(free_words() >= request_word_size, "Chunk too small.");
assert(free_below_committed_words() >= request_word_size, "Chunk not committed.");
MetaWord* const p = top();
_used_words += request_word_size;
SOMETIMES(verify();)
return p;
}
##gdb调试栈
#0 metaspace::Metachunk::allocate (this=0x7ffff00c3a20, request_word_size=76) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/memory/metaspace/metachunk.cpp:169
#1 0x00007ffff67a99d7 in metaspace::MetaspaceArena::allocate (this=0x7ffff00d20f0, requested_word_size=76)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/memory/metaspace/metaspaceArena.cpp:278
#2 0x00007ffff5fe6ead in ClassLoaderMetaspace::allocate (this=0x7ffff00d0410, word_size=76, mdType=Metaspace::NonClassType)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/memory/classLoaderMetaspace.cpp:96
#3 0x00007ffff67a7b07 in Metaspace::allocate (loader_data=0x7ffff00c6180, word_size=76, type=MetaspaceObj::TypeArrayU8Type)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/memory/metaspace.cpp:888
#4 0x00007ffff67a7bf4 in Metaspace::allocate (loader_data=0x7ffff00c6180, word_size=76, type=MetaspaceObj::TypeArrayU8Type, __the_thread__=0x7ffff0028920)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/memory/metaspace.cpp:908
#5 0x00007ffff6075e7f in Array<Klass*>::operator new (size=16, loader_data=0x7ffff00c6180, length=75, __the_thread__=0x7ffff0028920)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/oops/array.inline.hpp:36
#6 0x00007ffff6075a22 in MetadataFactory::new_array<Klass*> (loader_data=0x7ffff00c6180, length=75, __the_thread__=0x7ffff0028920)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/memory/metadataFactory.hpp:40
#7 0x00007ffff606bac7 in ConstantPool::allocate_resolved_klasses (this=0x7fffd9820c48, loader_data=0x7ffff00c6180, num_klasses=75, __the_thread__=0x7ffff0028920)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/oops/constantPool.cpp:212
#8 0x00007ffff5fb05da in ClassFileParser::parse_constant_pool (this=0x7ffff7bfddd0, stream=0x7ffff003c240, cp=0x7fffd9820c48, length=1098, __the_thread__=0x7ffff0028920)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/classFileParser.cpp:607
#9 0x00007ffff5fc1fe8 in ClassFileParser::parse_stream (this=0x7ffff7bfddd0, stream=0x7ffff003c240, __the_thread__=0x7ffff0028920)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/classFileParser.cpp:5720
#10 0x00007ffff5fc179d in ClassFileParser::ClassFileParser (this=0x7ffff7bfddd0, stream=0x7ffff003c240, name=0x7fffe0498220, loader_data=0x7ffff00c6180, cl_info=0x7ffff7bfe000,
pub_level=ClassFileParser::BROADCAST, __the_thread__=0x7ffff0028920) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/classFileParser.cpp:5590
#11 0x00007ffff66339a1 in KlassFactory::create_from_stream (stream=0x7ffff003c240, name=0x7fffe0498220, loader_data=0x7ffff00c6180, cl_info=..., __the_thread__=0x7ffff0028920)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/klassFactory.cpp:199
#12 0x00007ffff5fd2471 in ClassLoader::load_class (name=0x7fffe0498220, search_append_only=false, __the_thread__=0x7ffff0028920)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/classLoader.cpp:1222
#13 0x00007ffff6b10a5a in SystemDictionary::load_instance_class_impl (class_name=0x7fffe0498220, class_loader=..., __the_thread__=0x7ffff0028920)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/systemDictionary.cpp:1290
#14 0x00007ffff6b10e35 in SystemDictionary::load_instance_class (name_hash=1500110387, name=0x7fffe0498220, class_loader=..., __the_thread__=0x7ffff0028920)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/systemDictionary.cpp:1356
#15 0x00007ffff6b0eefd in SystemDictionary::resolve_instance_class_or_null (name=0x7fffe0498220, class_loader=..., protection_domain=..., __the_thread__=0x7ffff0028920)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/systemDictionary.cpp:724
#16 0x00007ffff6b0da82 in SystemDictionary::resolve_instance_class_or_null_helper (class_name=0x7fffe0498220, class_loader=..., protection_domain=...,
__the_thread__=0x7ffff0028920) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/systemDictionary.cpp:295
#17 0x00007ffff6b0d928 in SystemDictionary::resolve_or_null (class_name=0x7fffe0498220, class_loader=..., protection_domain=..., __the_thread__=0x7ffff0028920)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/systemDictionary.cpp:278
#18 0x00007ffff6b0d86b in SystemDictionary::resolve_or_fail (class_name=0x7fffe0498220, class_loader=..., protection_domain=..., throw_error=true, __the_thread__=0x7ffff0028920)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/systemDictionary.cpp:264
#19 0x00007ffff5da8a1a in SystemDictionary::resolve_or_fail (class_name=0x7fffe0498220, throw_error=true, __the_thread__=0x7ffff0028920)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/systemDictionary.hpp:100
#20 0x00007ffff6bf767c in vmClasses::resolve (id=vmClassID::ClassLoader_klass_knum, __the_thread__=0x7ffff0028920)
at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/vmClasses.cpp:99
#21 0x00007ffff6bf777a in vmClasses::resolve_until (limit_id=vmClassID::SoftReference_klass_knum, start_id=@0x7ffff7bfe8f0: vmClassID::Cloneable_klass_knum,
__the_thread__=0x7ffff0028920) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/vmClasses.cpp:108
#22 0x00007ffff6bf817e in vmClasses::resolve_through (last_id=vmClassID::Reference_klass_knum, start_id=@0x7ffff7bfe8f0: vmClassID::Cloneable_klass_knum,
__the_thread__=0x7ffff0028920) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/vmClasses.hpp:64
#23 0x00007ffff6bf7a07 in vmClasses::resolve_all (__the_thread__=0x7ffff0028920) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/vmClasses.cpp:168
#24 0x00007ffff6b11cf6 in SystemDictionary::initialize (__the_thread__=0x7ffff0028920) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/classfile/systemDictionary.cpp:1654
#25 0x00007ffff6b96cbf in Universe::genesis (__the_thread__=0x7ffff0028920) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/memory/universe.cpp:335
#26 0x00007ffff6b99117 in universe2_init () at /home/yym/openjdk17/jdk17-master/src/hotspot/share/memory/universe.cpp:928
#27 0x00007ffff633fe73 in init_globals () at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/init.cpp:134
#28 0x00007ffff6b610b7 in Threads::create_vm (args=0x7ffff7bfed50, canTryAgain=0x7ffff7bfec5b) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/thread.cpp:2852
#29 0x00007ffff644f775 in JNI_CreateJavaVM_inner (vm=0x7ffff7bfeda8, penv=0x7ffff7bfedb0, args=0x7ffff7bfed50) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/prims/jni.cpp:3624
#30 0x00007ffff644fac1 in JNI_CreateJavaVM (vm=0x7ffff7bfeda8, penv=0x7ffff7bfedb0, args=0x7ffff7bfed50) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/prims/jni.cpp:3712
#31 0x00007ffff7facd29 in InitializeJVM (pvm=0x7ffff7bfeda8, penv=0x7ffff7bfedb0, ifn=0x7ffff7bfee00) at /home/yym/openjdk17/jdk17-master/src/java.base/share/native/libjli/java.c:1541
#32 0x00007ffff7fa9623 in JavaMain (_args=0x7fffffffaed0) at /home/yym/openjdk17/jdk17-master/src/java.base/share/native/libjli/java.c:415
#33 0x00007ffff7fb08ab in ThreadJavaMain (args=0x7fffffffaed0) at /home/yym/openjdk17/jdk17-master/src/java.base/unix/native/libjli/java_md.c:651
#34 0x00007ffff7c94ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#35 0x00007ffff7d26850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81