20.2 基础类型的数组类型创建
该函数的入口在init.cpp->init_globals(),然后再调用universe.cpp->universe2_init()函数,实际执行的函数是Universe::genesis,所以从这开始源码的解析。解析前先了解一下Klass的概念,大家思考个问题:我们编写的java类在JVM中是以何种形式存在的呢?
答:其实他就是以Klass类存在的,Klass类就是java类在jvm中存在的形式。下图为Klass类的继承结构
图20-10
20.2.1 universe.cpp
20.2.1.1 Universe::genesis
void Universe::genesis(TRAPS) {ResourceMark rm;{ FlagSetting fs(_bootstrapping, true);{ MutexLocker mc(Compile_lock);// 计算Object类的虚函数表占用内存大小,实现细节看`章节20.2.1.2`compute_base_vtable_size();if (!UseSharedSpaces) {// 下面创建8个基础类型的数组类型的对象,细节看`章节20.2.2`_boolArrayKlassObj = TypeArrayKlass::create_klass(T_BOOLEAN, sizeof(jboolean), CHECK);_charArrayKlassObj = TypeArrayKlass::create_klass(T_CHAR, sizeof(jchar), CHECK);_singleArrayKlassObj = TypeArrayKlass::create_klass(T_FLOAT, sizeof(jfloat), CHECK);_doubleArrayKlassObj = TypeArrayKlass::create_klass(T_DOUBLE, sizeof(jdouble), CHECK);_byteArrayKlassObj = TypeArrayKlass::create_klass(T_BYTE, sizeof(jbyte), CHECK);_shortArrayKlassObj = TypeArrayKlass::create_klass(T_SHORT, sizeof(jshort), CHECK);_intArrayKlassObj = TypeArrayKlass::create_klass(T_INT, sizeof(jint), CHECK);_longArrayKlassObj = TypeArrayKlass::create_klass(T_LONG, sizeof(jlong), CHECK);// 将已创建的数组类型对象都存储到_typeArrayKlassObjs数组中,方便后面使用_typeArrayKlassObjs[T_BOOLEAN] = _boolArrayKlassObj;_typeArrayKlassObjs[T_CHAR] = _charArrayKlassObj;_typeArrayKlassObjs[T_FLOAT] = _singleArrayKlassObj;_typeArrayKlassObjs[T_DOUBLE] = _doubleArrayKlassObj;_typeArrayKlassObjs[T_BYTE] = _byteArrayKlassObj;_typeArrayKlassObjs[T_SHORT] = _shortArrayKlassObj;_typeArrayKlassObjs[T_INT] = _intArrayKlassObj;_typeArrayKlassObjs[T_LONG] = _longArrayKlassObj;// 根加载器的CLDClassLoaderData* null_cld = ClassLoaderData::the_null_class_loader_data();// 在null_cld中创建一个Array对象的数组,长度为2,元素默认值都为NULL_the_array_interfaces_array = MetadataFactory::new_array<Klass*>(null_cld, 2, NULL, CHECK);// 在null_cld中创建一个Array对象的数组,长度为0_the_empty_int_array = MetadataFactory::new_array<int>(null_cld, 0, CHECK);// 在null_cld中创建一个Array对象的数组,长度为0_the_empty_short_array = MetadataFactory::new_array<u2>(null_cld, 0, CHECK);// 在null_cld中创建一个Array对象的数组,长度为0_the_empty_method_array = MetadataFactory::new_array<Method*>(null_cld, 0, CHECK);// 在null_cld中创建一个Array对象的数组,长度为0_the_empty_klass_array = MetadataFactory::new_array<Klass*>(null_cld, 0, CHECK);}}// 下面代码逻辑将放在 `章节20.4` 和 `章节20.5` 中讲// 将系统类(System/Object/Class/String等等)表示的字符串都以符号symbol的对象存储到虚拟机的符号表中,关于符号表的介绍,可以看`章节19.2`,为什么要创建符号表呢?因为这些系统类是公共的,后面都会用到的,所以统一放符号表中。这块实现看`章节20.4`vmSymbols::initialize(CHECK);// 各字典(其实就是一个hash table)和基础类的预加载,字典主要用在后续类加载过程中,比如占位符字典表、解析出错表,具体实现细节看`章节20.5`SystemDictionary::initialize(CHECK);// 取出 Object KlassKlass* ok = SystemDictionary::Object_klass();// 把 null 和 -2147483648 作为字符串,预先存储到字符串表_the_null_string = StringTable::intern("null", CHECK);_the_min_jint_string = StringTable::intern("-2147483648", CHECK);if (UseSharedSpaces) {// Verify shared interfaces array.assert(_the_array_interfaces_array->at(0) ==SystemDictionary::Cloneable_klass(), "u3");assert(_the_array_interfaces_array->at(1) ==SystemDictionary::Serializable_klass(), "u3");} else {// 设置共享的接口类数组,0号位是Cloneable klass,1号位是Serializable klass_the_array_interfaces_array->at_put(0, SystemDictionary::Cloneable_klass());_the_array_interfaces_array->at_put(1, SystemDictionary::Serializable_klass());}// 设置基础类型的数组类型的超类为Objectinitialize_basic_type_klass(boolArrayKlassObj(), CHECK);initialize_basic_type_klass(charArrayKlassObj(), CHECK);initialize_basic_type_klass(singleArrayKlassObj(), CHECK);initialize_basic_type_klass(doubleArrayKlassObj(), CHECK);initialize_basic_type_klass(byteArrayKlassObj(), CHECK);initialize_basic_type_klass(shortArrayKlassObj(), CHECK);initialize_basic_type_klass(intArrayKlassObj(), CHECK);initialize_basic_type_klass(longArrayKlassObj(), CHECK);} // end of core bootstrapping// Object 数组类型 Klass_objectArrayKlassObj = InstanceKlass::cast(SystemDictionary::Object_klass())->array_klass(1, CHECK);// 将该类型添加到其兄弟类型的列表中_objectArrayKlassObj->append_to_sibling_list();// 针对 jdk1.3、1.4、1.5或更高版本的特殊几个类的加载// Only 1.3 or later has the java.lang.Shutdown class.// Only 1.4 or later has the java.lang.CharSequence interface.// Only 1.5 or later has the java.lang.management.MemoryUsage class.if (JDK_Version::is_partially_initialized()) {uint8_t jdk_version;Klass* k = SystemDictionary::resolve_or_null(vmSymbols::java_lang_management_MemoryUsage(), THREAD);CLEAR_PENDING_EXCEPTION; // ignore exceptionsif (k == NULL) {k = SystemDictionary::resolve_or_null(vmSymbols::java_lang_CharSequence(), THREAD);CLEAR_PENDING_EXCEPTION; // ignore exceptionsif (k == NULL) {k = SystemDictionary::resolve_or_null(vmSymbols::java_lang_Shutdown(), THREAD);CLEAR_PENDING_EXCEPTION; // ignore exceptionsif (k == NULL) {jdk_version = 2;} else {jdk_version = 3;}} else {jdk_version = 4;}} else {jdk_version = 5;}JDK_Version::fully_initialize(jdk_version);}#ifdef ASSERTif (FullGCALot) { // GC相关,先忽略,后续针对GC专题来讲// Allocate an array of dummy objects.// We'd like these to be at the bottom of the old generation,// so that when we free one and then collect,// (almost) the whole heap moves// and we find out if we actually update all the oops correctly.// But we can't allocate directly in the old generation,// so we allocate wherever, and hope that the first collection// moves these objects to the bottom of the old generation.// We can allocate directly in the permanent generation, so we do.int size;if (UseConcMarkSweepGC) {warning("Using +FullGCALot with concurrent mark sweep gc ""will not force all objects to relocate");size = FullGCALotDummies;} else {size = FullGCALotDummies * 2;}objArrayOop naked_array = oopFactory::new_objArray(SystemDictionary::Object_klass(), size, CHECK);objArrayHandle dummy_array(THREAD, naked_array);int i = 0;while (i < size) {// Allocate dummy in old generationoop dummy = InstanceKlass::cast(SystemDictionary::Object_klass())->allocate_instance(CHECK);dummy_array->obj_at_put(i++, dummy);}{// Only modify the global variable inside the mutex.// If we had a race to here, the other dummy_array instances// and their elements just get dropped on the floor, which is fine.MutexLocker ml(FullGCALot_lock);if (_fullgc_alot_dummy_array == NULL) {_fullgc_alot_dummy_array = dummy_array();}}assert(i == _fullgc_alot_dummy_array->length(), "just checking");}#endif// Initialize dependency array for null class loaderClassLoaderData::the_null_class_loader_data()->init_dependencies(CHECK);}
20.2.1.2 Universe::compute_base_vtable_size
void Universe::compute_base_vtable_size() {// 计算Object类的虚函数表占用内存大小_base_vtable_size = ClassLoader::compute_Object_vtable();
}
classLoader.cpp
在Java中所有的类都是默认继承自Object类的,所以这里要计算Object类的初始 vtable的大小,默认JDK_1_2_Object_vtable_size = 5,为什么是5呢?因为Object提供了5个可使用和继承的方法,如下:
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
protected native Object clone() throws CloneNotSupportedException;
public String toString() {
return getClass().getName() + “@” + Integer.toHexString(hashCode());
}
protected void finalize() throws Throwable { }
int ClassLoader::compute_Object_vtable() {int JDK_1_2_Object_vtable_size = 5; // 5个// 占用内存为5 * 每个的大小(vtableEntry::size())return JDK_1_2_Object_vtable_size * vtableEntry::size();
}
20.2.2 typeArrayKlass.hpp/cpp
20.2.2.1 create_klass
const char* TypeArrayKlass::external_name(BasicType type) {switch (type) {// 看着下面这些[开头的类型是不是很熟悉,在Java中打印基础类型的数组对象时,就会出现下面符号中的一种,也就是虚拟机层面给各基础类型数组的类型简称case T_BOOLEAN: return "[Z";case T_CHAR: return "[C";case T_FLOAT: return "[F";case T_DOUBLE: return "[D";case T_BYTE: return "[B";case T_SHORT: return "[S";case T_INT: return "[I";case T_LONG: return "[J";default: ShouldNotReachHere();}return NULL;
}static inline Klass* create_klass(BasicType type, int scale, TRAPS) {// 创建type类型的基础类型的数组类型TypeArrayKlass* tak = create_klass(type, external_name(type), CHECK_NULL);assert(scale == (1 << tak->log2_element_size()), "scale must check out");return tak;}TypeArrayKlass* TypeArrayKlass::create_klass(BasicType type,const char* name_str, TRAPS) {Symbol* sym = NULL;if (name_str != NULL) {// 把name_str先创建一个符号,并加入到符号表SymbolTable中,看到符号表是不是眼熟,可以回来看`章节19.2`sym = SymbolTable::new_permanent_symbol(name_str, CHECK_NULL);}// 拿到null加载器的CLD,也就是Java中所谓的根加载器ClassLoaderData* null_loader_data = ClassLoaderData::the_null_class_loader_data();// 给数组类型的Klass分配内存,看`章节20.2.2.2`TypeArrayKlass* ak = TypeArrayKlass::allocate(null_loader_data, type, sym, CHECK_NULL);// 将当前创建的klass ak,添加到根加载器的CLD的klasses链表中null_loader_data->add_class(ak);// Call complete_create_array_klass after all instance variables have been initialized./** 通过Java我们知道,每个类都有一个对应的java.lang.Class对象,同理在JVM中也需要有这么一个对象,在JVM中,* 该对象用InstanceMirrorKlass的对象来表示,所以这一步就是创建该数组类型的InstanceMirrorKlass对象。* 由`图20-10`,得到InstanceMirrorKlass是继承自InstanceKlass的,并且在InstanceMirrorKlass中定义了很多* 操作InstanceKlass类对象的函数,所以在Java中,java.lang.Class可以用来做反射处理。*/complete_create_array_klass(ak, ak->super(), CHECK_NULL);return ak;
}
20.2.2.2 allocate
TypeArrayKlass* TypeArrayKlass::allocate(ClassLoaderData* loader_data, BasicType type, Symbol* name, TRAPS) {assert(TypeArrayKlass::header_size() <= InstanceKlass::header_size(),"array klasses must be same size as InstanceKlass");// 先计算要分配的内存大小int size = ArrayKlass::static_size(TypeArrayKlass::header_size());// 在元空间中分配内存,new 操作看`章节20.2.3.1`,TypeArrayKlass 构造函数实现看`章节20.2.2.3`return new (loader_data, size, THREAD) TypeArrayKlass(type, name);
}
20.2.2.3 TypeArrayKlass
TypeArrayKlass::TypeArrayKlass(BasicType type, Symbol* name) : ArrayKlass(name) { // 调用父类构造函数set_layout_helper(array_layout_helper(type));assert(oop_is_array(), "sanity");assert(oop_is_typeArray(), "sanity");// 设置该数组的最大长度set_max_length(arrayOopDesc::max_array_length(type));assert(size() >= TypeArrayKlass::header_size(), "bad size");// 设置该数组类的CLD为根加载器Data,也就是说基础数组类型的加载都是通过根加载器去加载的set_class_loader_data(ClassLoaderData::the_null_class_loader_data());
}
父类ArrayKlass构造函数
ArrayKlass::ArrayKlass(Symbol* name) {set_name(name);// 设置名字// 这里默认设置超类为NULLset_super(Universe::is_bootstrapping() ? (Klass*)NULL : SystemDictionary::Object_klass());set_layout_helper(Klass::_lh_neutral_value);set_dimension(1);set_higher_dimension(NULL);set_lower_dimension(NULL);set_component_mirror(NULL);// Object类的vtable大小,这个在`章节20.2.1.2`中有介绍int vtable_size = Universe::base_vtable_size();set_vtable_length(vtable_size);set_is_cloneable(); // All arrays are considered to be cloneable (See JLS 20.1.5)JFR_ONLY(INIT_ID(this);)
}
20.2.3 klass.cpp
20.2.3.1 new创建并分配内存
void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw() {// 细节看`章节20.2.4.1`return Metaspace::allocate(loader_data, word_size, /*read_only*/false,MetaspaceObj::ClassType, THREAD);
}
20.2.4 metaspace.cpp
20.2.4.1 allocate
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}assert(loader_data != NULL, "Should never pass around a NULL loader_data. ""ClassLoaderData::the_null_class_loader_data() should have been used.");// DumpSharedSpaces表示多Java进程共享,这里不涉及多Java进程共享,这一步不会走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;}// 确定要分配的内存内容的类型,类的创建都是ClassTypeMetadataType mdtype = (type == MetaspaceObj::ClassType) ? ClassType : NonClassType;// 尝试分配元数据。loader_data->metaspace_non_null()函数(实现细节看`章节20.3`)可以拿到Metaspace的指针指向的元空间对象,然后再通过allocate函数在元空间中分配内存,allocate分配看后面描述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);// 分配失败,可能是内存不足,所以启动GCif (is_init_completed()) {// 尝试GC 清空一些内存result = Universe::heap()->collector_policy()->satisfy_failed_metadata_allocation(loader_data, word_size, mdtype);}}// 启动GC 后还是失败,那就直接报告oom错误if (result == NULL) {report_metadata_oome(loader_data, word_size, type, mdtype, CHECK_NULL);}// Zero initialize.Copy::fill_to_aligned_words((HeapWord*)result, word_size, 0);// 返回分配后的内存首地址return result;
}
allocate 从SpaceManager中分配内存
MetaWord* Metaspace::allocate(size_t word_size, MetadataType mdtype) {// class_vsm() 和 vsm() 取出的都是SpaceManager对象,直接往下看if (is_class_space_allocation(mdtype)) {return class_vsm()->allocate(word_size);} else {return vsm()->allocate(word_size);}
}
从SpaceManager中分配
MetaWord* SpaceManager::allocate(size_t word_size) {MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag);size_t raw_word_size = get_raw_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. JJJif (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) {assert_lock_strong(_lock);
#ifdef ASSERTif (Metadebug::test_metadata_failure()) {return NULL;}
#endif// Is there space in the current chunk?MetaWord* result = NULL;// For DumpSharedSpaces, only allocate out of the current chunk which is// never null because we gave it the size we wanted. Caller reports out// of memory if this returns null.if (DumpSharedSpaces) {assert(current_chunk() != NULL, "should never happen");inc_used_metrics(word_size);return current_chunk()->allocate(word_size); // caller handles null result}// current_chunk() 函数拿到当前在用的chunk,这个chunk就是`章节20.3.2`中 initialize_first_chunk()函数实现里面设置的,然后再通过allocate分配,怎么分配的呢,继续往下看if (current_chunk() != NULL) {result = current_chunk()->allocate(word_size);}// 分配不成功,肯定是容量不够了,这一步就是扩容if (result == NULL) {result = grow_and_allocate(word_size);}if (result != NULL) {inc_used_metrics(word_size);assert(result != (MetaWord*) chunks_in_use(MediumIndex),"Head of the list is being allocated");}return result;
}
从Metachunk中分配
看代码实现,非常简单,就是把_top上移word_size个字,读者只需把它理解成一个带刻度的水缸,往里面加水时,刻度值上升,就这么简单。
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;
}
20.3 创建和分配Metaspace
为什么要创建Metaspace呢?
答:在元空间分配元数据内存时都需要元空间对象Metaspace实例来操作?在第十八章
中虽然有对元空间的创建与分配讲解,但是并没有对元空间实际存储数据逻辑和真正的内存空间分配描述,第十八章
只是描述了对元空间实际分配元数据内存前的一些准备工作,比如划出一块大的空间作为元空间、再将大空间划成一块块小的chunk、再通过链表管理起来等等。这一章节就是讲具体的元数据的分配。
20.3.1 classLoaderData.cpp
20.3.1.1 metaspace_non_null
Metaspace* ClassLoaderData::metaspace_non_null() {assert(!DumpSharedSpaces, "wrong metaspace!");// If the metaspace has not been allocated, create a new one. Might want// to create smaller arena for Reflection class loaders also.// The reason for the delayed allocation is because some class loaders are// simply for delegating with no metadata of their own.if (_metaspace == NULL) {MutexLockerEx ml(metaspace_lock(), Mutex::_no_safepoint_check_flag);// Check again if metaspace has been allocated while we were getting this lock.if (_metaspace != NULL) {return _metaspace;}if (this == the_null_class_loader_data()) {assert (class_loader() == NULL, "Must be");// 直接看这一行,Metaspace的创建看`章节20.3.2`set_metaspace(new Metaspace(_metaspace_lock, Metaspace::BootMetaspaceType));} else if (is_anonymous()) {if (TraceClassLoaderData && Verbose && class_loader() != NULL) {tty->print_cr("is_anonymous: %s", class_loader()->klass()->internal_name());}set_metaspace(new Metaspace(_metaspace_lock, Metaspace::AnonymousMetaspaceType));} else if (class_loader()->is_a(SystemDictionary::reflect_DelegatingClassLoader_klass())) {if (TraceClassLoaderData && Verbose && class_loader() != NULL) {tty->print_cr("is_reflection: %s", class_loader()->klass()->internal_name());}set_metaspace(new Metaspace(_metaspace_lock, Metaspace::ReflectionMetaspaceType));} else {set_metaspace(new Metaspace(_metaspace_lock, Metaspace::StandardMetaspaceType));}}// 把创建后的metaspace对象实例返回return _metaspace;
}
20.3.2 metaspace.cpp
20.3.2.1 构造函数和初始始化
构造函数
Metaspace::Metaspace(Mutex* lock, MetaspaceType type) {initialize(lock, type);
}
初始化函数
void Metaspace::initialize(Mutex* lock, MetaspaceType type) {verify_global_initialization();// 创建 SpaceManager 对象,分配元数据(主要是符号、字符串等)需要用到,本章节的后面有对这对象创建的讲解_vsm = new SpaceManager(NonClassType, lock);if (using_class_space()) {// 创建 SpaceManager 对象,分配类时需要用到_class_vsm = new SpaceManager(ClassType, lock);} else {_class_vsm = NULL;}MutexLockerEx cl(SpaceManager::expand_lock(), Mutex::_no_safepoint_check_flag);// 这一步就是从`第十八章`中分配的空闲chunk链表中找出一个适合当前分配大小(前面讲过,MetaChunk块有三种大小,取一个适合自己的就行)的MetaChunk块对象,并把它添加到 SpaceManager 对象(在这里表示 _vsm)来管理initialize_first_chunk(type, NonClassType);// 同上,这是取出一块MetaChunk来存放类数据,并用 SpaceManager 对象(在这里表示 _class_vsm)管理if (using_class_space()) {initialize_first_chunk(type, ClassType);}_alloc_record_head = NULL;_alloc_record_tail = NULL;
}
创建SpaceManager对象
SpaceManager对象就是管理元数据分配在哪个chunk,这个对象的创建很简单,就是做一些初始值的赋值
SpaceManager::SpaceManager(Metaspace::MetadataType mdtype,Mutex* lock) :_mdtype(mdtype),_allocated_blocks_words(0),_allocated_chunks_words(0),_allocated_chunks_count(0),_lock(lock)
{initialize();
}void SpaceManager::initialize() {Metadebug::init_allocation_fail_alot_count();for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {_chunks_in_use[i] = NULL; // 把所有chunk已用状态设置为null}_current_chunk = NULL;if (TraceMetadataChunkAllocation && Verbose) {gclog_or_tty->print_cr("SpaceManager(): " PTR_FORMAT, this);}
}