快捷搜索:  汽车  科技

jvm内存模型设计原理(OOP-klass模型对类的描叙及类加载)

jvm内存模型设计原理(OOP-klass模型对类的描叙及类加载)class Klass : public Metadata { friend class VMStructs; friend class JVMCIVMStructs; .......... // Class name. Instance classes: java/lang/String etc. Array classes: [I // [Ljava/lang/String; etc. Set to zero for all other kinds of classes. Symbol* _name; ......... // Array of all secondary supertypes Array<Klass*>* _secondary_supers; // Ordered list of all prima

一、oop-klass描叙1、介绍

在JVM内存用到了oop-klass模型来描叙对应的类及对象:oop(ordinary object ponter,普通对象指针),其是用来描叙对象的实例信息。klass,其是JVM内部用来描叙类的信息的,例如java类的继承信息,成员方法等信息。同时JVM还有一种类型来封装对oop类型的行为-handle。

jvm内存模型设计原理(OOP-klass模型对类的描叙及类加载)(1)

2、handle

class Handle VALUE_OBJ_CLASS_SPEC { private: oop* _handle; protected: oop obj() const { return _handle == NULL ? (oop)NULL : *_handle; } oop non_null_obj() const { assert(_handle != NULL "resolving NULL handle"); return *_handle; } public: // Constructors Handle() { _handle = NULL; } Handle(oop obj); Handle(Thread* thread oop obj); // General access oop operator () () const { return obj(); } oop operator -> () const { return non_null_obj(); } bool operator == (oop o) const { return obj() == o; } bool operator == (const Handle& h) const { return obj() == h.obj(); } // Null checks bool is_null() const { return _handle == NULL; } bool not_null() const { return _handle != NULL; } // Debugging void print() { obj()->print(); } Handle(oop *handle bool dummy) { _handle = handle; } oop* raw_value() { return _handle; } static oop raw_resolve(oop *handle) { return handle == NULL ? (oop)NULL : *handle; } }; 复制代码

​ 可以看到其持有指向oop的指针,同时其的一些方法内部都是去处理的oop。

3、oop

class oop { oopDesc* _o; void register_oop(); void unregister_oop(); // friend class markOop; public: void set_obj(const void* p) { raw_set_obj(p); if (CheckUnhandledOops) register_oop(); } void raw_set_obj(const void* p) { _o = (oopDesc*)p; } oop() { set_obj(NULL); } oop(const oop& o) { set_obj(o.obj()); } ...... oopDesc* obj() const volatile { return _o; } // General access oopDesc* operator->() const { return obj(); } bool operator==(const oop o) const { return obj() == o.obj(); } bool operator==(void *p) const { return obj() == p; } ........ 复制代码

​ oop是持有指向oopDesc的指针,oopDesc就是描叙类信息。

1)、oopDesc

class oopDesc { friend class VMStructs; friend class JVMCIVMStructs; private: volatile markOop _mark; union _metadata { Klass* _klass; narrowKlass _compressed_klass; } _metadata; ...... 复制代码

这个就是描叙一个对象的基本结构。其中一个是对象头,就是我们一般看并发锁的书的时候说的需要获取的对象头,然后还有一个就是元数据结构_metadata,可以看到其有两个类型_klass&_compressed_klass。

Klass* oopDesc::klass() const { if (UseCompressedClassPointers) { return Klass::decode_klass_not_null(_metadata._compressed_klass); } else { return _metadata._klass; } } 复制代码

这里一个就是一般类型,一个就是压缩类型,通过UseCompressedClassPointers参数开启。

typedef class markOopDesc* markOop; typedef class oopDesc* oop; typedef class instanceOopDesc* instanceOop; typedef class arrayOopDesc* arrayOop; typedef class objArrayOopDesc* objArrayOop; typedef class typeArrayOopDesc* typeArrayOop; 复制代码

​ 这些就是oopDesc的子类。这里我们主要关注两个类型markOopDesc描叙对象的头信息、instanceOopDesc描叙类的实例信息。

2)、markOopDesc

// The markOop describes the header of an object. // // Note that the mark is not a real oop but just a word. // It is placed in the oop hierarchy for historical reasons. // // Bit-format of an object header (most significant first big endian layout below): // // 32 bits: // -------- // hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object) // JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object) // size:32 ------------------------------------------>| (CMS free block) // PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object) // // 64 bits: // -------- // unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object) // JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object) // PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object) // size:64 ----------------------------------------------------->| (CMS free block) // // unused:25 hash:31 -->| cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && normal object) // JavaThread*:54 epoch:2 cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && biased object) // narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object) // unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block) .......... class markOopDesc: public oopDesc { private: // Conversion uintptr_t value() const { return (uintptr_t) this; } public: // Constants enum { age_bits = 4 lock_bits = 2 biased_lock_bits = 1 max_hash_bits = BitsPerWord - age_bits - lock_bits - biased_lock_bits hash_bits = max_hash_bits > 31 ? 31 : max_hash_bits cms_bits = LP64_ONLY(1) NOT_LP64(0) epoch_bits = 2 }; // The biased locking code currently requires that the age bits be // contiguous to the lock bits. enum { lock_shift = 0 biased_lock_shift = lock_bits age_shift = lock_bits biased_lock_bits cms_shift = age_shift age_bits hash_shift = cms_shift cms_bits epoch_shift = hash_shift }; ............ 复制代码

​ 通过32位的注释可以看到主要有四种类型normal object一般对象、biased object偏向锁,可以看到其有一个参数JavaThread,指向对应偏向的对象,然后就是CMS free block这个应该就是释放的对象,可以看到其不含任何内容、CMS promoted object这个从名称看猜测其应该是晋升对象,应该是指的晋升到了老年低?(可以看到其已经没有表示年龄age这个属性了)。同时我们可以看到表示年龄的是4位,也就是最大是为15。

3)、instanceOopDesc

// An instanceOop is an instance of a Java Class // Evaluating "new HashTable()" will create an instanceOop. class instanceOopDesc : public oopDesc { public: // aligned header size. static int header_size() { return sizeof(instanceOopDesc)/HeapWordSize; } // If compressed the offset of the fields of the instance may not be aligned. static int base_offset_in_bytes() { // offset computation code breaks if UseCompressedClassPointers // only is true return (UseCompressedOops && UseCompressedClassPointers) ? klass_gap_offset_in_bytes() : sizeof(instanceOopDesc); } static bool contains_field_offset(int offset int nonstatic_field_size) { int base_in_bytes = base_offset_in_bytes(); return (offset >= base_in_bytes && (offset-base_in_bytes) < nonstatic_field_size * heapOopSize); } }; 复制代码

​ 这个就是表示对应类的实例对象,可以看到这个没有什么额外的内容,然后有个header_size()方法,这个通过注释就是看其头部的大小,因为可以是32位的一个可以是64位的。

4、klass

class Klass : public Metadata { friend class VMStructs; friend class JVMCIVMStructs; .......... // Class name. Instance classes: java/lang/String etc. Array classes: [I // [Ljava/lang/String; etc. Set to zero for all other kinds of classes. Symbol* _name; ......... // Array of all secondary supertypes Array<Klass*>* _secondary_supers; // Ordered list of all primary supertypes Klass* _primary_supers[_primary_super_limit]; // java/lang/Class instance mirroring this class oop _java_mirror; // Superclass Klass* _super; .......... // The VM's representation of the ClassLoader used to load this class. // Provide access the corresponding instance java.lang.ClassLoader. ClassLoaderData* _class_loader_data; 复制代码

​ 这个就是在JVM内部描叙Class信息的类,_name就是用来描叙类名称等的内容,这里还有一个oop的类型的_java_mirror,这里在<<揭秘Java虚拟机-JVM设计原理与实现>>这本书的解释是,klass对于类的描叙是提供给JVM内部访问的,而这个_java_mirror是提供给java的。

同时我们可以看到这个类是继承的Metadata元数据:

// This is the base class for an internal Class related metadata class Metadata : public MetaspaceObj { 复制代码

class MetaspaceObj { public: bool is_metaspace_object() const; bool is_shared() const; void print_address_on(outputStream* st) const; // nonvirtual address printing #define METASPACE_OBJ_TYPES_DO(f) \ f(Unknown) \ f(Class) \ f(Symbol) \ f(TypeArrayU1) \ f(TypeArrayU2) \ f(TypeArrayU4) \ f(TypeArrayU8) \ f(TypeArrayOther) \ f(Method) \ f(ConstMethod) \ f(MethodData) \ f(ConstantPool) \ f(ConstantPoolCache) \ f(Annotation) \ f(MethodCounters) \ f(Deallocated) 复制代码

​ 可以看到其又是继承的MetaspaceObj,所以其是在分配Metaspace中,同时属于Metaspace还有ConstantPool、ConstMethod这些也是在Metaspace。

​ 同时Klass类似前面的oopDesc其也有对应的子类,例如InstanceKlass、ArrayKlass。

1)、InstanceKlass

class InstanceKlass: public Klass { .......... protected: InstanceKlass(const ClassFileParser& parser unsigned kind); ...... // See "The Java Virtual Machine Specification" section 2.16.2-5 for a detailed description // of the class loading & initialization procedure and the use of the states. enum ClassState { allocated // allocated (but not yet linked) loaded // loaded and inserted in class hierarchy (but not linked yet) linked // successfully linked/verified (but not initialized yet) being_initialized // currently running class initializer fully_initialized // initialized (successfull final state) initialization_error // error happened during initialization }; protected: // Annotations for this class Annotations* _annotations; // Package this class is defined in PackageEntry* _package_entry; // Array classes holding elements of this class. Klass* volatile _array_klasses; // Constant pool for this class. ConstantPool* _constants; ..... Array<jushort>* _inner_classes; .......... u2 _static_oop_field_count;// number of static oop fields in this klass u2 _java_fields_count; // The number of declared Java fields int _nonstatic_oop_map_size;// size in words of nonstatic oop map blocks int _itable_len; // length of Java itable (in words) // _is_marked_dependent can be set concurrently thus cannot be part of the // _misc_flags. bool _is_marked_dependent; // used for marking during flushing and deoptimization bool _is_being_redefined; // used for locking redefinition ....... u2 _misc_flags; u2 _minor_version; // minor version number of class file u2 _major_version; // major version number of class file Thread* _init_thread; // Pointer to current thread doing initialization (to handle recusive initialization) OopMapCache* volatile _oop_map_cache; // OopMapCache for all methods in the klass (allocated lazily) MemberNameTable* _member_names; // Member names JNIid* _jni_ids; // First JNI identifier for static fields in this class jmethodID* volatile _methods_jmethod_ids; // jmethodIDs corresponding to method_idnum or NULL if none 复制代码

​ 这个就是用来描叙类信息的,我们可以看到定义了一个枚举ClassState来表示当前类的加载情况,例如:loaded(加载)、linked(链接)、fully_initialized(完成了类的初始化)。然后还有一些属性例如_annotations注解信息、_constants常量池信息等。

二、类的解析

1、起点 (openjdk\hotspot\src\share\vm\classfile\classLoader.cpp)

instanceKlassHandle ClassLoader::load_class(Symbol* name bool search_append_only TRAPS) { ....... ResourceMark rm(THREAD); HandleMark hm(THREAD); const char* const class_name = name->as_C_string(); EventMark m("loading class %s" class_name); ThreadProfilerMark tpm(ThreadProfilerMark::classLoaderRegion); const char* const file_name = file_name_for_class_name(class_name name->utf8_length()); assert(file_name != NULL "invariant"); ClassLoaderExt::Context context(class_name file_name THREAD); // Lookup stream for parsing .class file ClassFileStream* stream = NULL; s2 classpath_index = 0; ClassPathEntry* e = NULL; ......... // Load Attempt #2: [jimage | exploded build] if (!search_append_only && (NULL == stream)) { if (has_jrt_entry()) { e = _jrt_entry; stream = _jrt_entry->open_stream(file_name CHECK_NULL); if (!context.check(stream classpath_index)) { return NULL; } } else { // Exploded build - attempt to locate class in its defining module's location. assert(_exploded_entries != NULL "No exploded build entries present"); stream = search_module_entries(_exploded_entries class_name file_name CHECK_NULL); } } ......... if (NULL == stream) { if (DumpSharedSpaces) { tty->print_cr("Preload Warning: Cannot find %s" class_name); } return NULL; } stream->set_verify(context.should_verify(classpath_index)); ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); Handle protection_domain; instanceKlassHandle result = KlassFactory::create_from_stream(stream name loader_data protection_domain NULL // host_klass NULL // cp_patches ........ return context.record_result(name e classpath_index result THREAD); } 复制代码

​ 这个就是去加载解析.class文件:

jvm内存模型设计原理(OOP-klass模型对类的描叙及类加载)(2)

​ 可以看到我们目前处理的是BitSet,然后将其读取为stream(ClassFileStream)。

class instanceKlassHandle : public KlassHandle { public: /* Constructors */ instanceKlassHandle () : KlassHandle() {} instanceKlassHandle (const Klass* k) : KlassHandle(k) { assert(k == NULL || is_instanceKlass(k) "illegal type"); } instanceKlassHandle (Thread* thread const Klass* k) : KlassHandle(thread k) { assert(k == NULL || is_instanceKlass(k) "illegal type"); } /* Access to klass part */ InstanceKlass* operator () () const { return (InstanceKlass*)obj(); } InstanceKlass* operator -> () const { return (InstanceKlass*)obj(); } debug_only(bool is_instanceKlass(const Klass* k)); }; 复制代码

2)、然后再通过create_from_stream方法来解析:

instanceKlassHandle KlassFactory::create_from_stream(ClassFileStream* stream Symbol* name ClassLoaderData* loader_data Handle protection_domain const InstanceKlass* host_klass GrowableArray<Handle>* cp_patches TRAPS) { assert(stream != NULL "invariant"); assert(loader_data != NULL "invariant"); assert(THREAD->is_Java_thread() "must be a JavaThread"); ResourceMark rm; HandleMark hm; JvmtiCachedClassFileData* cached_class_file = NULL; ......... ClassFileParser parser(stream name loader_data protection_domain host_klass cp_patches ClassFileParser::BROADCAST // publicity level CHECK_NULL); instanceKlassHandle result = parser.create_instance_klass(old_stream != stream CHECK_NULL); if (result.is_null()) { return NULL; } ....... #if INCLUDE_CDS && INCLUDE_JVMTI if (DumpSharedSpaces) { assert(cached_class_file == NULL "Sanity"); // Archive the class stream data into the optional data section JvmtiCachedClassFileData *p; int len; const unsigned char *bytes; // event based tracing might set cached_class_file if ((bytes = result->get_cached_class_file_bytes()) != NULL) { len = result->get_cached_class_file_len(); } else { len = stream->length(); bytes = stream->buffer(); } p = (JvmtiCachedClassFileData*)MetaspaceShared::optional_data_space_alloc( offset_of(JvmtiCachedClassFileData data) len); p->length = len; memcpy(p->data bytes len); result->set_archived_class_data(p); } #endif return result; } 复制代码

​ 这里先解析获取parser,再通过其创建instanceKlassHandle(parser.create_instance_klass(old_stream != stream CHECK_NULL))。

3)、解析创建InstanceKlass

InstanceKlass* ClassFileParser::create_instance_klass(bool changed_by_loadhook TRAPS) { ......... InstanceKlass* const ik = InstanceKlass::allocate_instance_klass(*this CHECK_NULL); fill_instance_klass(ik changed_by_loadhook CHECK_NULL); ...... return ik; } 复制代码

InstanceKlass* InstanceKlass::allocate_instance_klass(const ClassFileParser& parser TRAPS) { const int size = InstanceKlass::size(parser.vtable_size() parser.itable_size() nonstatic_oop_map_size(parser.total_oop_map_count()) parser.is_interface() parser.is_anonymous() should_store_fingerprint()); const Symbol* const class_name = parser.class_name(); assert(class_name != NULL "invariant"); ClassLoaderData* loader_data = parser.loader_data(); assert(loader_data != NULL "invariant"); InstanceKlass* ik; // Allocation if (REF_NONE == parser.reference_type()) { if (class_name == vmSymbols::java_lang_Class()) { // mirror ik = new (loader_data size THREAD) InstanceMirrorKlass(parser); } else if (is_class_loader(class_name parser)) { // class loader ik = new (loader_data size THREAD) InstanceClassLoaderKlass(parser); } else { // normal ik = new (loader_data size THREAD) InstanceKlass(parser InstanceKlass::_misc_kind_other); } } else { // reference ik = new (loader_data size THREAD) InstanceRefKlass(parser); } ...... // Add all classes to our internal class loader list here // including classes in the bootstrap (NULL) class loader. loader_data->add_class(ik publicize); Atomic::inc(&_total_instanceKlass_count); return ik; } 复制代码

​ 这里就是创建实例KlassInstanceKlass。本次会调用new (loader_data size THREAD) InstanceKlass(parser InstanceKlass::_misc_kind_other)。

void* Klass::operator new(size_t size ClassLoaderData* loader_data size_t word_size TRAPS) throw() { return Metaspace::allocate(loader_data word_size /*read_only*/false MetaspaceObj::ClassType THREAD); } 复制代码

​ 同时用于这里的类型入参是MetaspaceObj::ClassType 所以是会分配在_class_vsm。

然后再来看下fill_instance_klass(ik changed_by_loadhook CHECK_NULL):

void ClassFileParser::fill_instance_klass(InstanceKlass* ik bool changed_by_loadhook TRAPS) { ......... // Fill in information already parsed ik->set_should_verify_class(_need_verify); // Not yet: supers are done below to support the new subtype-checking fields ik->set_class_loader_data(_loader_data); ik->set_nonstatic_field_size(_field_info->nonstatic_field_size); ik->set_has_nonstatic_fields(_field_info->has_nonstatic_fields); ik->set_static_oop_field_count(_fac->count[STATIC_OOP]); .......... ik->set_name(_class_name); if (is_anonymous()) { // I am well known to myself ik->constants()->klass_at_put(_this_class_index ik); // eagerly resolve } ik->set_minor_version(_minor_version); ik->set_major_version(_major_version); ik->set_has_nonstatic_concrete_methods(_has_nonstatic_concrete_methods); ik->set_declares_nonstatic_concrete_methods(_declares_nonstatic_concrete_methods); if (_host_klass != NULL) { assert (ik->is_anonymous() "should be the same"); ik->set_host_klass(_host_klass); } // Set PackageEntry for this_klass oop cl = ik->class_loader(); Handle clh = Handle(THREAD java_lang_ClassLoader::non_reflection_class_loader(cl)); ClassLoaderData* cld = ClassLoaderData::class_loader_data_or_null(clh()); ik->set_package(cld CHECK); .... // check if this class can access its superinterfaces check_super_interface_access(ik CHECK); // check if this class overrides any final method check_final_method_override(ik CHECK); ..... // Obtain java.lang.Module Handle module_handle(THREAD JNIHandles::resolve(module_entry->module())); // Allocate mirror and initialize static fields // The create_mirror() call will also call compute_modifiers() java_lang_Class::create_mirror(ik _loader_data->class_loader() module_handle _protection_domain CHECK); ...... // it's official set_klass(ik); debug_only(ik->verify();) } 复制代码

​ 这个就是填充InstanceKlass的信息,然后我们看下create_mirror方法:

void java_lang_Class::create_mirror(KlassHandle k Handle class_loader Handle module Handle protection_domain TRAPS) { ..... int computed_modifiers = k->compute_modifier_flags(CHECK); k->set_modifier_flags(computed_modifiers); // Class_klass has to be loaded because it is used to allocate // the mirror. if (SystemDictionary::Class_klass_loaded()) { // Allocate mirror (java.lang.Class instance) Handle mirror = InstanceMirrorKlass::cast(SystemDictionary::Class_klass())->allocate_instance(k CHECK); // Setup indirection from mirror->klass if (!k.is_null()) { java_lang_Class::set_klass(mirror() k()); } InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(mirror->klass()); ........... } 复制代码3)、parser方法解析:

ClassFileParser::ClassFileParser(ClassFileStream* stream Symbol* name ClassLoaderData* loader_data Handle protection_domain const InstanceKlass* host_klass GrowableArray<Handle>* cp_patches Publicity pub_level TRAPS) : ....... // Figure out whether we can skip format checking (matching classic VM behavior) if (DumpSharedSpaces) { // verify == true means it's a 'remote' class (i.e. non-boot class) // Verification decision is based on BytecodeVerificationRemote flag // for those classes. _need_verify = (stream->need_verify()) ? BytecodeVerificationRemote : BytecodeVerificationLocal; } else { _need_verify = Verifier::should_verify_for(_loader_data->class_loader() stream->need_verify()); } // synch back verification state to stream stream->set_verify(_need_verify); // Check if verification needs to be relaxed for this class file // Do not restrict it to jdk1.0 or jdk1.1 to maintain backward compatibility (4982376) _relax_verify = relax_format_check_for(_loader_data); parse_stream(stream CHECK); post_process_parsed_stream(stream _cp CHECK); } 复制代码

void ClassFileParser::parse_stream(const ClassFileStream* const stream TRAPS) { // BEGIN STREAM PARSING stream->guarantee_more(8 CHECK); // magic major minor // Magic value const u4 magic = stream->get_u4_fast(); // Version numbers _minor_version = stream->get_u2_fast(); _major_version = stream->get_u2_fast(); .......... _cp = ConstantPool::allocate(_loader_data cp_size CHECK); ConstantPool* const cp = _cp; parse_constant_pool(stream cp cp_size CHECK); // ACCESS FLAGS stream->guarantee_more(8 CHECK); // flags this_class super_class infs_len // Access flags jint flags; // JVM_ACC_MODULE is defined in JDK-9 and later. if (_major_version >= JAVA_9_VERSION) { flags = stream->get_u2_fast() & (JVM_RECOGNIZED_CLASS_MODIFIERS | JVM_ACC_MODULE); } else { flags = stream->get_u2_fast() & JVM_RECOGNIZED_CLASS_MODIFIERS; } ........ _access_flags.set_flags(flags); // This class and superclass _this_class_index = stream->get_u2_fast(); ..... Symbol* const class_name_in_cp = cp->klass_name_at(_this_class_index); assert(class_name_in_cp != NULL "class_name can't be null"); // Update _class_name which could be null previously // to reflect the name in the constant pool _class_name = class_name_in_cp; ......... // SUPERKLASS _super_class_index = stream->get_u2_fast(); _super_klass = parse_super_class(cp _super_class_index _need_verify CHECK); // Interfaces _itfs_len = stream->get_u2_fast(); parse_interfaces(stream _itfs_len cp &_has_nonstatic_concrete_methods CHECK); ....... // Fields (offsets are filled in later) _fac = new FieldAllocationCount(); parse_fields(stream _access_flags.is_interface() _fac cp cp_size &_java_fields_count CHECK); ...... // Methods AccessFlags promoted_flags; parse_methods(stream _access_flags.is_interface() &promoted_flags &_has_final_method &_declares_nonstatic_concrete_methods CHECK); ........ // Additional attributes/annotations _parsed_annotations = new ClassAnnotationCollector(); parse_classfile_attributes(stream cp _parsed_annotations CHECK); ...... } 复制代码

​ 这里先会通过ConstantPool::allocate申请常量此的空间,再通过parse_constant_pool(stream cp cp_size CHECK)具体解析,然后解析Methods、Fields这些。

ConstantPool* ConstantPool::allocate(ClassLoaderData* loader_data int length TRAPS) { ....... int size = ConstantPool::size(length); return new (loader_data size false MetaspaceObj::ConstantPoolType THREAD) ConstantPool(tags); } 复制代码

void* MetaspaceObj::operator new(size_t size ClassLoaderData* loader_data size_t word_size bool read_only MetaspaceObj::Type type TRAPS) throw() { // Klass has it's own operator new return Metaspace::allocate(loader_data word_size read_only type THREAD); } 复制代码

MetaWord* Metaspace::allocate(ClassLoaderData* loader_data size_t word_size bool read_only MetaspaceObj::Type type TRAPS) { ....... MetadataType mdtype = (type == MetaspaceObj::ClassType) ? ClassType : NonClassType; // Try to allocate metadata. MetaWord* result = loader_data->metaspace_non_null()->allocate(word_size mdtype); ....... // Zero initialize. 空间清零 Copy::fill_to_words((HeapWord*)result word_size 0); return result; } 复制代码

public: enum MetadataType { ClassType NonClassType MetadataTypeCount }; enum MetaspaceType { StandardMetaspaceType BootMetaspaceType ROMetaspaceType ReadWriteMetaspaceType AnonymousMetaspaceType ReflectionMetaspaceType }; 复制代码

​ 可以看到这里MetadataType有ClassType、NonClassType、MetadataTypeCount,这里主要是因为在JVM中又会将MetaspaceType空间再分为两种:

Metaspace* ClassLoaderData::metaspace_non_null() { Metaspace* metaspace = load_ptr_acquire(&_metaspace); if (metaspace == NULL) { MutexlockerEx ml(_metaspace_lock Mutex::_no_safepoint_check_flag); // Check if _metaspace got allocated while we were waiting for this lock. if ((metaspace = _metaspace) == NULL) { if (this == the_null_class_loader_data()) { assert (class_loader() == NULL "Must be"); metaspace = new Metaspace(_metaspace_lock Metaspace::BootMetaspaceType); } else if (is_anonymous()) { if (class_loader() != NULL) { log_trace(class loader data)("is_anonymous: %s" class_loader()->klass()->internal_name()); } metaspace = new Metaspace(_metaspace_lock Metaspace::AnonymousMetaspaceType); } else if (class_loader()->is_a(SystemDictionary::reflect_DelegatingClassLoader_klass())) { if (class_loader() != NULL) { log_trace(class loader data)("is_reflection: %s" class_loader()->klass()->internal_name()); } metaspace = new Metaspace(_metaspace_lock Metaspace::ReflectionMetaspaceType); } else { metaspace = new Metaspace(_metaspace_lock Metaspace::StandardMetaspaceType); } // Ensure _metaspace is stable since it is examined without a lock OrderAccess::release_store_ptr(&_metaspace metaspace); } } return metaspace; } 复制代码

​ 这里可以看到会创建对应的空间类型new Metaspace

Metaspace::Metaspace(Mutex* lock MetaspaceType type) { initialize(lock type); } 复制代码

void Metaspace::initialize(Mutex* lock MetaspaceType type) { verify_global_initialization(); // Allocate SpaceManager for metadata objects. _vsm = new SpaceManager(NonClassType lock); if (using_class_space()) { // Allocate SpaceManager for classes. _class_vsm = new SpaceManager(ClassType lock); } MutexLockerEx cl(SpaceManager::expand_lock() Mutex::_no_safepoint_check_flag); // Allocate chunk for metadata objects initialize_first_chunk(type NonClassType); // Allocate chunk for class metadata objects if (using_class_space()) { initialize_first_chunk(type ClassType); } _alloc_record_head = NULL; _alloc_record_tail = NULL; } 复制代码

​ 可以看到,这里会创建两种空间管理_vsm、如果是Class的话会使用_class_vsm

// SpaceManager - used by Metaspace to handle allocations class SpaceManager : public CHeapObj<mtClass> { friend class Metaspace; friend class Metadebug; private: // protects allocations Mutex* const _lock; // Type of metadata allocated. Metaspace::MetadataType _mdtype; 复制代码

​ 使用这个SpaceManager来进行分配管理,可以看到这里是锁的Mutex,用于竞争分配空间。下面我们再来看空间分配:

MetaWord* Metaspace::allocate(size_t word_size MetadataType mdtype) { // DumpSharedSpaces doesn't use class metadata area (yet) // Also don't use class_vsm() unless UseCompressedClassPointers is true. if (is_class_space_allocation(mdtype)) { return class_vsm()->allocate(word_size); } else { return vsm()->allocate(word_size); } } 复制代码

可以看到这里会根据类型从不同的vsm中分配:

MetaWord* SpaceManager::allocate(size_t word_size) { MutexLockerEx cl(lock() Mutex::_no_safepoint_check_flag); size_t raw_word_size = get_allocation_word_size(word_size); BlockFreelist* fl = block_freelists(); MetaWord* p = NULL; // Allocation from the dictionary is expensive in the sense that // the dictionary has to be searched for a size. Don't allocate // from the dictionary until it starts to get fat. Is this // a reasonable policy? Maybe an skinny dictionary is fast enough // for allocations. Do some profiling. JJJ if (fl != NULL && fl->total_size() > allocation_from_dictionary_limit) { p = fl->get_block(raw_word_size); } if (p == NULL) { p = allocate_work(raw_word_size); } return p; } 复制代码

MetaWord* SpaceManager::allocate_work(size_t word_size) { ..... // Is there space in the current chunk? MetaWord* result = NULL; ........ if (current_chunk() != NULL) { result = current_chunk()->allocate(word_size); } ....... return result; } 复制代码

MetaWord* Metachunk::allocate(size_t word_size) { MetaWord* result = NULL; // If available bump the pointer to allocate. if (free_word_size() >= word_size) { result = _top; _top = _top word_size; } return result; } 复制代码二、类实例的创建1、起点

​ openjdk\hotspot\src\share\vm\interpreter\interpreterRuntime.cpp

// Allocation IRT_ENTRY(void InterpreterRuntime::_new(JavaThread* thread ConstantPool* pool int index)) Klass* k_oop = pool->klass_at(index CHECK); instanceKlassHandle klass (THREAD k_oop); // Make sure we are not instantiating an abstract klass klass->check_valid_for_instantiation(true CHECK); // Make sure klass is initialized klass->initialize(CHECK); // At this point the class may not be fully initialized // because of recursive initialization. If it is fully // initialized & has_finalized is not set we rewrite // it into its fast version (Note: no locking is needed // here since this is an atomic byte write and can be // done more than once). // // Note: In case of classes with has_finalized we don't // rewrite since that saves us an extra check in // the fast version which then would call the // slow version anyway (and do a call back into // Java). // If we have a breakpoint then we don't rewrite // because the _breakpoint bytecode would be lost. oop obj = klass->allocate_instance(CHECK); thread->set_vm_result(obj); IRT_END 复制代码

​ 我们以这个为起点,看下类实例oop对象是怎样申请空间的:

2、这里首先是klass->initialize(CHECK)

// See "The Virtual Machine Specification" section 2.16.5 for a detailed explanation of the class initialization // process. The step comments refers to the procedure described in that section. // Note: implementation moved to static method to expose the this pointer. void InstanceKlass::initialize(TRAPS) { if (this->should_be_initialized()) { HandleMark hm(THREAD); instanceKlassHandle this_k(THREAD this); initialize_impl(this_k CHECK); // Note: at this point the class may be initialized // OR it may be in the state of being initialized // in case of recursive initialization! } else { assert(is_initialized() "sanity check"); } } 复制代码

​ 这里首先是类加载,即instanceKlassHandle对象的创建初始化initialize_impl(this_k CHECK):

void InstanceKlass::initialize_impl(instanceKlassHandle this_k TRAPS) { // Make sure klass is linked (verified) before initialization // A class could already be verified since it has been reflected upon. this_k->link_class(CHECK); ...... bool wait = false; // refer to the JVM book page 47 for description of steps // Step 1 { oop init_lock = this_k->init_lock(); ObjectLocker ol(init_lock THREAD init_lock != NULL); Thread *self = THREAD; // it's passed the current thread // Step 2 // If we were to use wait() instead of waitInterruptibly() then // we might end up throwing IE from link/symbol resolution sites // that aren't expected to throw. This would wreak havoc. See 6320309. while(this_k->is_being_initialized() && !this_k->is_reentrant_initialization(self)) { wait = true; ol.waitUninterruptibly(CHECK); } // Step 3 if (this_k->is_being_initialized() && this_k->is_reentrant_initialization(self)) { DTRACE_CLASSINIT_PROBE_WAIT(recursive this_k() -1 wait); return; } // Step 4 if (this_k->is_initialized()) { DTRACE_CLASSINIT_PROBE_WAIT(concurrent this_k() -1 wait); return; } // Step 5 if (this_k->is_in_error_state()) { ........... } // Step 6 this_k->set_init_state(being_initialized); this_k->set_init_thread(self); } // Step 7 // Next if C is a class rather than an interface initialize it's super class and super // interfaces. if (!this_k->is_interface()) { Klass* super_klass = this_k->super(); if (super_klass != NULL && super_klass->should_be_initialized()) { super_klass->initialize(THREAD); } // If C implements any interface that declares a non-static concrete method // the initialization of C triggers initialization of its super interfaces. // Only need to recurse if has_nonstatic_concrete_methods which includes declaring and // having a superinterface that declares non-static concrete methods if (!HAS_PENDING_EXCEPTION && this_k->has_nonstatic_concrete_methods()) { this_k->initialize_super_interfaces(this_k THREAD); } ......... } // Look for aot compiled methods for this klass including class initializer. AOTLoader::load_for_klass(this_k THREAD); // Step 8 { ...... this_k->call_class_initializer(THREAD); } // Step 9 if (!HAS_PENDING_EXCEPTION) { this_k->set_initialization_state_and_notify(fully_initialized CHECK); ...... } else { // Step 10 and 11 Handle e(THREAD PENDING_EXCEPTION); CLEAR_PENDING_EXCEPTION; // JVMTI has already reported the pending exception // JVMTI internal flag reset is needed in order to report ExceptionInInitializerError ......... } DTRACE_CLASSINIT_PROBE_WAIT(end this_k() -1 wait); } 复制代码

这里对应前面的枚举类型就是类的加载过程。加载后,接下来就是oop对象的实例化创建oop obj = klass->allocate_instance(CHECK);:

3、klass->allocate_instance(CHECK)

instanceOop InstanceKlass::allocate_instance(TRAPS) { bool has_finalizer_flag = has_finalizer(); // Query before possible GC int size = size_helper(); // Query before forming handle. KlassHandle h_k(THREAD this); instanceOop i; i = (instanceOop)CollectedHeap::obj_allocate(h_k size CHECK_NULL); if (has_finalizer_flag && !RegisterFinalizersAtInit) { i = register_finalizer(i CHECK_NULL); } return i; } 复制代码

typedef class oopDesc* oop; typedef class instanceOopDesc* instanceOop; typedef class arrayOopDesc* arrayOop; typedef class objArrayOopDesc* objArrayOop; typedef class typeArrayOopDesc* typeArrayOop; 复制代码

​ 这里是通过obj_allocate创建instanceOop实例对象。

4、obj_allocate

​ openjdk\hotspot\src\share\vm\gc\shared\collectedHeap.inline.hpp

oop CollectedHeap::obj_allocate(KlassHandle klass int size TRAPS) { debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active() "Allocation during gc not allowed"); assert(size >= 0 "int won't convert to size_t"); HeapWord* obj = common_mem_allocate_init(klass size CHECK_NULL); post_allocation_setup_obj(klass obj size); NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj size)); return (oop)obj; } 复制代码

​ 然后再通过common_mem_allocate_init方法进行空间分配及初始化:

HeapWord* CollectedHeap::common_mem_allocate_init(KlassHandle klass size_t size TRAPS) { HeapWord* obj = common_mem_allocate_noinit(klass size CHECK_NULL); init_obj(obj size); return obj; } 复制代码5、common_mem_allocate_noinit

HeapWord* CollectedHeap::common_mem_allocate_noinit(KlassHandle klass size_t size TRAPS) { HeapWord* result = NULL; if (UseTLAB) { result = allocate_from_tlab(klass THREAD size); if (result != NULL) { assert(!HAS_PENDING_EXCEPTION "Unexpected exception will result in uninitialized storage"); return result; } } bool gc_overhead_limit_was_exceeded = false; result = Universe::heap()->mem_allocate(size &gc_overhead_limit_was_exceeded); if (result != NULL) { NOT_PRODUCT(Universe::heap()-> check_for_non_bad_heap_word_value(result size)); assert(!HAS_PENDING_EXCEPTION "Unexpected exception will result in uninitialized storage"); THREAD->incr_allocated_bytes(size * HeapWordSize); AllocTracer::send_allocation_outside_tlab_event(klass size * HeapWordSize); return result; } ....... } 复制代码

​ 这里首先是判断UseTLAB,即是否分配先分配在与线程相关的空间TLAB上(能更快分配,因为如果分配在堆上,由于堆随便那个线程都能申请,所以会加锁,而如果分配在与线程相关的TLAB上,就没有锁竞争,就能更快的分配)

6、allocate_from_tlab

HeapWord* CollectedHeap::allocate_from_tlab(KlassHandle klass Thread* thread size_t size) { assert(UseTLAB "should use UseTLAB"); HeapWord* obj = thread->tlab().allocate(size); if (obj != NULL) { return obj; } // Otherwise... return allocate_from_tlab_slow(klass thread size); } 复制代码

// Thread-Local Allocation Buffer (TLAB) support ThreadLocalAllocBuffer& tlab() { return _tlab; } 复制代码

inline HeapWord* ThreadLocalAllocBuffer::allocate(size_t size) { invariants(); HeapWord* obj = top(); if (pointer_delta(end() obj) >= size) { // successful thread-local allocation #ifdef ASSERT // Skip mangling the space corresponding to the object header to // ensure that the returned space is not considered parsable by // any concurrent GC thread. size_t hdr_size = oopDesc::header_size(); Copy::fill_to_words(obj hdr_size size - hdr_size badHeapWordVal); #endif // ASSERT // This addition is safe because we know that top is // at least size below end so the add can't wrap. set_top(obj size); invariants(); return obj; } return NULL; } 复制代码

HeapWord* top() const { return _top; } 复制代码

​ 这里首先是通过pointer_delta方法看有没有足够的空间分配,有的话就分配,再通过set_top(obj size);设置新的_top位置。

如果上面这个分配失败,再进入慢分配allocate_from_tlab_slow(KlassHandle klass Thread* thread size_t size)

HeapWord* CollectedHeap::allocate_from_tlab_slow(KlassHandle klass Thread* thread size_t size) { ..... // Allocate a new TLAB... HeapWord* obj = Universe::heap()->allocate_new_tlab(new_tlab_size); if (obj == NULL) { return NULL; } AllocTracer::send_allocation_in_new_tlab_event(klass new_tlab_size * HeapWordSize size * HeapWordSize); if (ZeroTLAB) { // ..and clear it. Copy::zero_to_words(obj new_tlab_size); } else { // ...and zap just allocated object. #ifdef ASSERT // Skip mangling the space corresponding to the object header to // ensure that the returned space is not considered parsable by // any concurrent GC thread. size_t hdr_size = oopDesc::header_size(); Copy::fill_to_words(obj hdr_size new_tlab_size - hdr_size badHeapWordVal); #endif // ASSERT } thread->tlab().fill(obj obj size new_tlab_size); return obj; } 复制代码

​ 可以看到这里还有一个参数ZeroTLAB来表示是否将分配到空间清零。

​ 下面我们再通过将UseTLAB设置为false来从堆上分配 Universe::heap()->mem_allocate。

7、Universe::heap()->mem_allocate

// The particular choice of collected heap. static CollectedHeap* heap() { return _collectedHeap; } 复制代码

HeapWord* GenCollectedHeap::mem_allocate(size_t size bool* gc_overhead_limit_was_exceeded) { return gen_policy()->mem_allocate_work(size false /* is_tlab */ gc_overhead_limit_was_exceeded); } 复制代码

jvm内存模型设计原理(OOP-klass模型对类的描叙及类加载)(3)

​ 可以看到目前的策略是标记清除。

HeapWord* GenCollectorPolicy::mem_allocate_work(size_t size bool is_tlab bool* gc_overhead_limit_was_exceeded) { GenCollectedHeap *gch = GenCollectedHeap::heap(); ............. HeapWord* result = NULL; // Loop until the allocation is satisfied or unsatisfied after GC. for (uint try_count = 1 gclocker_stalled_count = 0; /* return or throw */; try_count = 1) { HandleMark hm; // Discard any handles allocated in each iteration. // First allocation attempt is lock-free. Generation *young = gch->young_gen(); if (young->should_allocate(size is_tlab)) { result = young->par_allocate(size is_tlab); if (result != NULL) { assert(gch->is_in_reserved(result) "result not in heap"); return result; } } uint gc_count_before; // Read inside the Heap_lock locked region. { MutexLocker ml(Heap_lock); log_trace(gc alloc)("GenCollectorPolicy::mem_allocate_work: attempting locked slow path allocation"); // Note that only large objects get a shot at being // allocated in later generations. bool first_only = ! should_try_older_generation_allocation(size); result = gch->attempt_allocation(size is_tlab first_only); if (result != NULL) { assert(gch->is_in_reserved(result) "result not in heap"); return result; } if (GCLocker::is_active_and_needs_gc()) { if (is_tlab) { return NULL; // Caller will retry allocating individual object. } if (!gch->is_maximal_no_gc()) { // Try and expand heap to satisfy request. result = expand_heap_and_allocate(size is_tlab); // Result could be null if we are out of space. if (result != NULL) { return result; } } ....... } } 复制代码1)、这里首先是判断是不能再年轻代分配

// First allocation attempt is lock-free. Generation *young = gch->young_gen(); if (young->should_allocate(size is_tlab)) { result = young->par_allocate(size is_tlab); if (result != NULL) { assert(gch->is_in_reserved(result) "result not in heap"); return result; } } 复制代码

如果年轻代能分配就通过par_allocate方法去分配空间:

HeapWord* DefNewGeneration::par_allocate(size_t word_size bool is_tlab) { HeapWord* res = eden()->par_allocate(word_size); if (CMSEdenChunksRecordAlways && _old_gen != NULL) { _old_gen->sample_eden_chunk(); } return res; } 复制代码

// Accessing spaces ContiguousSpace* eden() const { return _eden_space; } ContiguousSpace* from() const { return _from_space; } ContiguousSpace* to() const { return _to_space; } 复制代码

// This version is lock-free. inline HeapWord* ContiguousSpace::par_allocate_impl(size_t size) { do { HeapWord* obj = top(); if (pointer_delta(end() obj) >= size) { HeapWord* new_top = obj size; HeapWord* result = (HeapWord*)Atomic::cmpxchg_ptr(new_top top_addr() obj); // result can be one of two: // the old top value: the exchange succeeded // otherwise: the new value of the top is returned. if (result == obj) { assert(is_aligned(obj) && is_aligned(new_top) "checking alignment"); return obj; } } else { return NULL; } } while (true); } 复制代码

​ 这里就是在_eden_space区分配,可以看到这里有个注释,现在是没有使用锁的,其是使用Atomic::cmpxchg_ptr操作,即CAS去修改申请的,然后通过while循环。同时上面的DefNewGeneration::par_allocate我们需要注意,其是在eden()申请,并不会如果eden()不够再去from()或to()申请。

下面我们看下如果young->should_allocate(size is_tlab)不满足,通过gch->attempt_allocation(size is_tlab first_only)方法申请的过程。

2)、attempt_allocation

MutexLocker ml(Heap_lock); log_trace(gc alloc)("GenCollectorPolicy::mem_allocate_work: attempting locked slow path allocation"); // Note that only large objects get a shot at being // allocated in later generations. bool first_only = ! should_try_older_generation_allocation(size); result = gch->attempt_allocation(size is_tlab first_only); if (result != NULL) { assert(gch->is_in_reserved(result) "result not in heap"); return result; } 复制代码

​ 可以看到这里会使用锁。

HeapWord* GenCollectedHeap::attempt_allocation(size_t size bool is_tlab bool first_only) { HeapWord* res = NULL; if (_young_gen->should_allocate(size is_tlab)) { res = _young_gen->allocate(size is_tlab); if (res != NULL || first_only) { return res; } } if (_old_gen->should_allocate(size is_tlab)) { res = _old_gen->allocate(size is_tlab); } return res; } 复制代码

​ 这里会再次判断_young_gen是否能分配,再次尝试分配。

HeapWord* DefNewGeneration::allocate(size_t word_size bool is_tlab) { // This is the slow-path allocation for the DefNewGeneration. // Most allocations are fast-path in compiled code. // We try to allocate from the eden. If that works we are happy. // Note that since DefNewGeneration supports lock-free allocation we // have to use it here as well. HeapWord* result = eden()->par_allocate(word_size); if (result != NULL) { if (CMSEdenChunksRecordAlways && _old_gen != NULL) { _old_gen->sample_eden_chunk(); } } else { // If the eden is full and the last collection bailed out we are running // out of heap space and we try to allocate the from-space too. // allocate_from_space can't be inlined because that would introduce a // circular dependency at compile time. result = allocate_from_space(word_size); } return result; } 复制代码

HeapWord* DefNewGeneration::allocate_from_space(size_t size) { bool should_try_alloc = should_allocate_from_space() || GCLocker::is_active_and_needs_gc(); // If the Heap_lock is not locked by this thread this will be called // again later with the Heap_lock held. bool do_alloc = should_try_alloc && (Heap_lock->owned_by_self() || (SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread())); HeapWord* result = NULL; if (do_alloc) { result = from()->allocate(size); } return result; } 复制代码

​ 可以看到这里如果在eden()区分配失败,是可能再在from()区分配的。现在我们看下在老年代的分配:

3)、HeapWord* TenuredGeneration::allocate

HeapWord* TenuredGeneration::allocate(size_t word_size bool is_tlab) { assert(!is_tlab "TenuredGeneration does not support TLAB allocation"); return _the_space->allocate(word_size); } 复制代码

inline HeapWord* OffsetTableContigSpace::allocate(size_t size) { HeapWord* res = ContiguousSpace::allocate(size); if (res != NULL) { _offsets.alloc_block(res size); } return res; } 复制代码

// This version requires locking. 这种分配是需要锁的 inline HeapWord* ContiguousSpace::allocate_impl(size_t size) { assert(Heap_lock->owned_by_self() || (SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread()) "not locked"); HeapWord* obj = top(); if (pointer_delta(end() obj) >= size) { HeapWord* new_top = obj size; set_top(new_top); assert(is_aligned(obj) && is_aligned(new_top) "checking alignment"); return obj; } else { return NULL; } } 复制代码

​ 以上就是我们对JVM类对象oopp的空间申请创建的过程这里流程的梳理。


作者:Feverasa
链接:https://juejin.cn/post/6936877801128198174
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

猜您喜欢: