对于class的分析,对于java中的所有的数据实际都是Class对象,不管是原生类型还是对象类型;
从jdk8+开始 ,对于class对象的数据在jvm内存就存储在了metaData区域,通过openjdk中的c++源码也可以看到其结论
对应的c++源码为:
// 在src/hotspot/share/oops/klass.hpp 中存在 Klass 的定义 class Klass : public Metadata{ // ..... }
通过Klass的继承关系也可以看到其 属于 Metadata对象数据
https://www.tuicool.com/articles/jIfiUb
以下是对 Class#getSuperClass的源码分析 以及 对 java.lang.String#intern 源码分析
/** * 对于 java.lang.Class.java * 在 openjdk中存在 jdk15-0dabbdfd97e6/src/java.base/share/native/libjava/Class.c * 在其中存在 registerNatives方法 会将所有定义的methods 进行注册,对于methods中存在了相关 java native method 和 c++ 对应的 method * * 在其中有一个特殊的方法为 getSuperclass ,其在methods中没有其对应的c++ method而是调用的jni.h中定义的GetSuperclass * 在jni.cpp中存在以下代码 为当前方法的具体实现 * JNI_ENTRY(jclass, jni_GetSuperclass(JNIEnv *env, jclass sub)) * JNIWrapper("GetSuperclass"); * * HOTSPOT_JNI_GETSUPERCLASS_ENTRY(env, sub); * * jclass obj = NULL; * DT_RETURN_MARK(GetSuperclass, jclass, (const jclass&)obj); * * // 对于 JNIHandles::resolve_non_null 是将java对象转换为 c++ 的 oop * // 对于 JNIHandles::resolve_non_null 是 src/hotspot/share/runtime/jniHandles.hpp 中定义的静态方法 * // 在对应的 src/hotspot/share/runtime/jniHandles.inline.hpp 中存在其具体的实现方法 inline oop JNIHandles::resolve_non_null(jobject handle) * // 其实际是调用的 inline oop JNIHandles::resolve_impl(jobject handle) 最终是返回的 AccessInternal::OopLoadProxy 来生成一个对象 * oop mirror = JNIHandles::resolve_non_null(sub); * // primitive classes return NULL * // 对于原生类型的判断,如果为原生类型则直接返回空原因为原生类型不可能存在super * if (java_lang_Class::is_primitive(mirror)) return NULL; // int.class.getSuperclass(); * * // Rules of Class.getSuperClass as implemented by KLass::java_super: * // arrays return Object * // interfaces return NULL * // proper classes return Klass::super() * // 如果是一个非原生类型的数据,会将其转换为 Klass * // 对于klass的集成关系可以看到 Klass -> Metadata -> MetaspaceObj * // 根据其继承关系可以看出为什么说 java的class 存在于 Meta空间中; * Klass* k = java_lang_Class::as_Klass(mirror); * if (k->is_interface()) return NULL; * * // return mirror for superclass * Klass* super = k->java_super(); * // super2 is the value computed by the compiler's getSuperClass intrinsic: * debug_only(Klass* super2 = ( k->is_array_klass() * ? SystemDictionary::Object_klass() * : k->super() ) ); * assert(super == super2, * "java_super computation depends on interface, array, other super"); * obj = (super == NULL) ? NULL : (jclass) JNIHandles::make_local(super->java_mirror()); * return obj; * JNI_END */ /** * 在 src/hotspot/share/memory/allocation.hpp 中定义了 以下几类内存空间 * ResourceObj : For objects allocated in the resource area (see resourceArea.hpp). * CHeapObj : For objects allocated in the C-heap (managed by: free & malloc and tracked with NMT) * StackObj : For objects allocated on the stack. * AllStatic : For classes used as name spaces. * MetaspaceObj : For classes in Metaspace (class data) * * 在 Metaspace中主要包含以下数据 * Class 编译后的 class数据 * Symbol 对应的java class 修饰符 Modifier.java * f(TypeArrayU1) * f(TypeArrayU2) * f(TypeArrayU4) * f(TypeArrayU8) * f(TypeArrayOther) // 定义的相关数组数据 * f(Method) // 方法签名 在 Method 中包含以下类型数据 ConstMethod / MethodData / MethodCounters 等相关数据 * f(ConstMethod) // 由于java中的方法当编译为class之后都是不可变的,对于这些数据都是不可变数据,因此数据常量数据 * f(MethodData) // 对于methodData中包含了以下数据 header klass 当前方法所属class/ 当前方法的大小 / 数据变量 * f(ConstantPool) // 常量数据 对于 constanPool 实际是由 ConstantPool + ConstantPoolCache 组成 * f(ConstantPoolCache) // 常量池缓存 * f(Annotations) // 注解 * f(MethodCounters) // * f(RecordComponent) * @param args */ /** * java.lang.String#intern() 分析 * 对于其native方法对应的是 src/java.base/share/native/libjava/String.c 中的 Java_java_lang_String_intern * -> jvm.h中的 JVM_InternString 其实现为 src/hotspot/share/prims/jvm.cpp 中的 JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str)) * JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str)) * JVMWrapper("JVM_InternString"); * JvmtiVMObjectAllocEventCollector oam; * if (str == NULL) return NULL; * oop string = JNIHandles::resolve_non_null(str); // 转换为 oop 对象 * oop result = StringTable::intern(string, CHECK_NULL); // 在这里实际调用的是stringTable.cpp 中的 oop StringTable::intern(oop string, TRAPS) * return (jstring) JNIHandles::make_local(env, result); * JVM_END * * // StringTable -> CHeapObj * oop StringTable::intern(oop string, TRAPS) { * if (string == NULL) return NULL; * ResourceMark rm(THREAD); * int length; * Handle h_string (THREAD, string); * jchar* chars = java_lang_String::as_unicode_string(string, length, * CHECK_NULL); * oop result = intern(h_string, chars, length, CHECK_NULL); // 对应的是 oop StringTable::intern(Handle string_or_null_h, const jchar* name, int len, TRAPS) * return result; * } * * oop StringTable::intern(Handle string_or_null_h, const jchar* name, int len, TRAPS) { * // shared table always uses java_lang_String::hash_code * unsigned int hash = java_lang_String::hash_code(name, len); // 将其转换为 hash值 * oop found_string = lookup_shared(name, len, hash); // * if (found_string != NULL) { * return found_string; * } * if (_alt_hash) { * hash = hash_string(name, len, true); * } * found_string = do_lookup(name, len, hash); * if (found_string != NULL) { * return found_string; * } * return do_intern(string_or_null_h, name, len, hash, THREAD); * } * // lookup_shared 实际就是 查找 _shared_table 中的数据, 如果查找不到则会在 _local_table 中进行查找 * // 最后会执行 oop StringTable::do_intern(Handle string_or_null_h, const jchar* name,int len, uintx hash, TRAPS) * oop StringTable::do_intern(Handle string_or_null_h, const jchar* name, * int len, uintx hash, TRAPS) { * HandleMark hm(THREAD); // cleanup strings created * Handle string_h; * // 处理当前 string 对象,保证其不为空,如果为空则会创建一个新的对象 * if (!string_or_null_h.is_null()) { * string_h = string_or_null_h; * } else { * string_h = java_lang_String::create_from_unicode(name, len, CHECK_NULL); * } * * // Deduplicate the string before it is interned. Note that we should never * // deduplicate a string after it has been interned. Doing so will counteract * // compiler optimizations done on e.g. interned string literals. * Universe::heap()->deduplicate_string(string_h()); // 执行重复数据清除,对于当前实现一般都是垃圾回收器来实现,所以在string对象创建之前进行处理 * * assert(java_lang_String::equals(string_h(), name, len), * "string must be properly initialized"); * assert(len == java_lang_String::length(string_h()), "Must be same length"); * * StringTableLookupOop lookup(THREAD, hash, string_h); * StringTableGet stg(THREAD); * * bool rehash_warning; * do { * // Callers have already looked up the String using the jchar* name, so just go to add. * WeakHandle<vm_string_table_data> wh = WeakHandle<vm_string_table_data>::create(string_h); // 创建一个weakHandle对象来存储当前String * // The hash table takes ownership of the WeakHandle, even if it's not inserted. * if (_local_table->insert(THREAD, lookup, wh, &rehash_warning)) { // 并在 _local_table 中写入当前数据,并和当前线程进行关联 * update_needs_rehash(rehash_warning); * return wh.resolve(); * } * // In case another thread did a concurrent add, return value already in the table. * // This could fail if the String got gc'ed concurrently, so loop back until success. * if (_local_table->get(THREAD, lookup, stg, &rehash_warning)) { * update_needs_rehash(rehash_warning); * return stg.get_res_oop(); * } * } while(true); * } * * @param args */ public static void main(String[] args) { Class<? super Integer> superclass = int.class.getSuperclass(); log.info("{}",superclass); assert superclass==null; }
对于intern 方法在源码中可以看出来实际就是利用了字符串数据共享的特点,首先会在jvm常量池中进行查找,其次会在当前线程共享数据中进行查找,如果都没有,则会创建新的string对象,并将其存放到constant-pool的缓存数据中,且首先会清除重复的字符串数据;
原文地址:java学习-类加载-Class以及对应的openjdk源码分析
本文链接地址:java学习-类加载-Class以及对应的openjdk源码分析,英雄不问来路,转载请注明出处,谢谢。
有话想说:那就赶紧去给我留言吧。
文章评论