【108期】面試官:你真的知道 Java 類是如何被加載的嗎?
閱讀本文大概需要 11 分鐘。
來自:https://yq.aliyun.com/articles/710407
一:前言
二:Java 類是如何被加載的
2.1:何時加載類
遇到new、getstatic、putstatic 等指令時。
對類進(jìn)行反射調(diào)用的時候。
初始化某個類的子類的時候。
虛擬機(jī)啟動時會先加載設(shè)置的程序主類。
使用JDK 1.7 的動態(tài)語言支持的時候。
2.2:怎么加載類
public?class?Test?{
????public?static?void?main(String[]?args)?throws?ClassNotFoundException?{
????????Test.class.getClassLoader().loadClass("com.wangxiandeng.test.Dog");
????}
}
2.3:JVM 是怎么加載類的
static?native?Class>?defineClass1(ClassLoader?loader,?String?name,?byte[]?b,?int?off,?int?len,
????????????????????????????????????????ProtectionDomain?pd,?String?source);?
JNIEXPORT?jclass?JNICALL
Java_java_lang_ClassLoader_defineClass1(JNIEnv?*env,
????????????????????????????????????????jclass?cls,
????????????????????????????????????????jobject?loader,
????????????????????????????????????????jstring?name,
????????????????????????????????????????jbyteArray?data,
????????????????????????????????????????jint?offset,
????????????????????????????????????????jint?length,
????????????????????????????????????????jobject?pd,
????????????????????????????????????????jstring?source)
{
????......
????result?=?JVM_DefineClassWithSource(env,?utfName,?loader,?body,?length,?pd,?utfSource);
????......
????return?result;
}
static?jclass?jvm_define_class_common(JNIEnv?*env,?const?char?*name,
??????????????????????????????????????jobject?loader,?const?jbyte?*buf,
??????????????????????????????????????jsize?len,?jobject?pd,?const?char?*source,
??????????????????????????????????????TRAPS)?{
??......
??ClassFileStream?st((u1*)buf,?len,?source,?ClassFileStream::verify);
??Handle?class_loader?(THREAD,?JNIHandles::resolve(loader));
??if?(UsePerfData)?{
????is_lock_held_by_thread(class_loader,
???????????????????????????ClassLoader::sync_JVMDefineClassLockFreeCounter(),
???????????????????????????THREAD);
??}
??Handle?protection_domain?(THREAD,?JNIHandles::resolve(pd));
??Klass*?k?=?SystemDictionary::resolve_from_stream(class_name,
???????????????????????????????????????????????????class_loader,
???????????????????????????????????????????????????protection_domain,
???????????????????????????????????????????????????&st,
???????????????????????????????????????????????????CHECK_NULL);
??......
??return?(jclass)?JNIHandles::make_local(env,?k->java_mirror());
}
class?InstanceKlass:?public?Klass?{
?protected:
??Annotations*????_annotations;
??......
??ConstantPool*?_constants;
??......
??Array*?_inner_classes;
??......
??Array*?_methods;
??Array*?_default_methods;
??......
??Array*??????_fields;
}
bool?DoObjectLock?=?true;
if?(is_parallelCapable(class_loader))?{
??DoObjectLock?=?false;
}
ClassLoaderData*?loader_data?=?register_loader(class_loader,?CHECK_NULL);
Handle?lockObject?=?compute_loader_lock_object(class_loader,?THREAD);
check_loader_lock_contention(lockObject,?THREAD);
ObjectLocker?ol(lockObject,?THREAD,?DoObjectLock);
InstanceKlass*?k?=?NULL;
k?=?KlassFactory::create_from_stream(st,
?????????????????????????????????????????class_name,
?????????????????????????????????????????loader_data,
?????????????????????????????????????????protection_domain,
?????????????????????????????????????????NULL,?//?host_klass
?????????????????????????????????????????NULL,?//?cp_patches
?????????????????????????????????????????CHECK_NULL);
if?(is_parallelCapable(class_loader))?{
??InstanceKlass*?defined_k?=?find_or_define_instance_class(h_name,?class_loader,?k,?THREAD);
??if?(!HAS_PENDING_EXCEPTION?&&?defined_k?!=?k)?{
????//?If?a?parallel?capable?class?loader?already?defined?this?class,?register?'k'?for?cleanup.
????assert(defined_k?!=?NULL,?"Should?have?a?klass?if?there's?no?exception");
????loader_data->add_to_deallocate_list(k);
????k?=?defined_k;
??}
}?else?{
??define_instance_class(k,?THREAD);
}
如果沒有查詢到,那么就將剛剛加載的 InstanceKlass 注冊到 ClassLoader的 Dictionary 中 中。
雖然并行加載不會鎖住ClassLoader,但是會在注冊 InstanceKlass 時對 SystemDictionary 加鎖,所以不需要擔(dān)心InstanceKlass 在注冊時的并發(fā)操作。
ClassFileParser?parser(stream,
???????????????????????name,
???????????????????????loader_data,
???????????????????????protection_domain,
???????????????????????host_klass,
???????????????????????cp_patches,
???????????????????????ClassFileParser::BROADCAST,?//?publicity?level
???????????????????????CHECK_NULL);
InstanceKlass*?result?=?parser.create_instance_klass(old_stream?!=?stream,?CHECK_NULL);
2.4:不得不說的ClassFileParser
InstanceKlass*?const?ik?=
????InstanceKlass::allocate_instance_klass(*this,?CHECK_NULL);
我們先來說道說道第一件事,為 InstanceKlass 分配內(nèi)存。
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(parser.is_anonymous()));
ClassLoaderData*?loader_data?=?parser.loader_data();
InstanceKlass*?ik;
ik?=?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,?MetaspaceObj::ClassType,?THREAD);
}
?????????????????????????????MetaspaceObj::Type?type,?TRAPS)?{
??......
??MetadataType?mdtype?=?(type?==?MetaspaceObj::ClassType)???ClassType?:?NonClassType;
??......
??MetaWord*?result?=?loader_data->metaspace_non_null()->allocate(word_size,?mdtype);
??......
??return?result;
}
void?ClassFileParser::fill_instance_klass(InstanceKlass*?ik,?bool?changed_by_loadhook,?TRAPS)?{
??.....
??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);
??......
??java_lang_Class::create_mirror(ik,
?????????????????????????????????Handle(THREAD,?_loader_data->class_loader()),
?????????????????????????????????module_handle,
?????????????????????????????????_protection_domain,
?????????????????????????????????CHECK);
}
三:再談雙親委派
public?class?CustomClassloader?extends?URLClassLoader?{
????public?CustomClassloader(URL[]?urls)?{
????????super(urls);
????}
????@Override
????protected?Class>?loadClass(String?name,?boolean?resolve)?throws?ClassNotFoundException?{
????????if?(name.startsWith("com.wangxiandeng"))?{
????????????return?findClass(name);
????????}
????????return?super.loadClass(name,?resolve);
????}
}
public?class?Test?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????URL?url[]?=?new?URL[1];
????????url[0]?=?Thread.currentThread().getContextClassLoader().getResource("");
????????CustomClassloader?customClassloader?=?new?CustomClassloader(url);
????????Class?clazz?=?customClassloader.loadClass("com.wangxiandeng.Student");
????????Student?student?=?(Student)?clazz.newInstance();
????}
}
Exception?in?thread?"main"?java.lang.ClassCastException:
??????com.wangxiandeng.Student?cannot?be?cast?to?com.wangxiandeng.Student
??public?static?void?main(java.lang.String[])?throws?java.lang.Exception;
????descriptor:?([Ljava/lang/String;)V
????flags:?ACC_PUBLIC,?ACC_STATIC
????Code:
??????stack=4,?locals=5,?args_size=1
?????????0:?iconst_1
?????????1:?anewarray?????#2??????????????????//?class?java/net/URL
?????????4:?astore_1
?????????5:?aload_1
?????????6:?iconst_0
?????????7:?invokestatic??#3??????????????????//?Method?java/lang/Thread.currentThread:()Ljava/lang/Thread;
????????10:?invokevirtual?#4??????????????????//?Method?java/lang/Thread.getContextClassLoader:()Ljava/lang/ClassLoader;
????????13:?ldc???????????#5??????????????????//?String
????????15:?invokevirtual?#6??????????????????//?Method?java/lang/ClassLoader.getResource:(Ljava/lang/String;)Ljava/net/URL;
????????18:?aastore
????????19:?new???????????#7??????????????????//?class?com/wangxiandeng/classloader/CustomClassloader
????????22:?dup
????????23:?aload_1
????????24:?invokespecial?#8??????????????????//?Method?com/wangxiandeng/classloader/CustomClassloader."":([Ljava/net/URL;)V
????????27:?astore_2
????????28:?aload_2
????????29:?ldc???????????#9??????????????????//?String?com.wangxiandeng.Student
????????31:?invokevirtual?#10?????????????????//?Method?com/wangxiandeng/classloader/CustomClassloader.loadClass:(Ljava/lang/String;)Ljava/lang/Class;
????????34:?astore_3
????????35:?aload_3
????????36:?invokevirtual?#11?????????????????//?Method?java/lang/Class.newInstance:()Ljava/lang/Object;
????????39:?checkcast?????#12?????????????????//?class?com/wangxiandeng/Student
????????42:?astore????????4
????????44:?return
#12 = Class #52 // com/wangxiandeng/StudentCASE(_checkcast):
????if?(STACK_OBJECT(-1)?!=?NULL)?{
??????VERIFY_OOP(STACK_OBJECT(-1));
??????//?拿到 checkcast 指令后的操作數(shù),本例子中即 Student.Class 在常量池中的索引:#12
??????u2?index?=?Bytes::get_Java_u2(pc+1);
??????//?如果常量池還沒有解析,先進(jìn)行解析,即將常量池中的符號引用替換成直接引用,
??????//此時就會觸發(fā)Student.Class?的加載
??????if?(METHOD->constants()->tag_at(index).is_unresolved_klass())?{
????????CALL_VM(InterpreterRuntime::quicken_io_cc(THREAD),?handle_exception);
??????}
??????//?獲取上一步系統(tǒng)加載的Student.Class?對應(yīng)的?InstanceKlass
??????Klass*?klassOf?=?(Klass*)?METHOD->constants()->resolved_klass_at(index);
??????//?獲取要強(qiáng)轉(zhuǎn)的對象的實(shí)際類型,即我們自己手動加載的Student.Class?對應(yīng)的?InstanceKlass
??????Klass*?objKlass?=?STACK_OBJECT(-1)->klass();?//?ebx
??????//?現(xiàn)在就比較簡單了,直接看看上面的兩個InstanceKlass指針內(nèi)容是否相同
??????//?不同的情況下則判斷是否存在繼承關(guān)系
??????if?(objKlass?!=?klassOf?&&?!objKlass->is_subtype_of(klassOf))?{
????????//?Decrement?counter?at?checkcast.
????????BI_PROFILE_SUBTYPECHECK_FAILED(objKlass);
????????ResourceMark?rm(THREAD);
????????char*?message?=?SharedRuntime::generate_class_cast_message(
??????????objKlass,?klassOf);
????????VM_JAVA_ERROR(vmSymbols::java_lang_ClassCastException(),?message,?note_classCheck_trap);
??????}
??????//?Profile?checkcast?with?null_seen?and?receiver.
??????BI_PROFILE_UPDATE_CHECKCAST(/*null_seen=*/false,?objKlass);
????}?else?{
??????//?Profile?checkcast?with?null_seen?and?receiver.
??????BI_PROFILE_UPDATE_CHECKCAST(/*null_seen=*/true,?NULL);
????}
//?如果常量池還沒有解析,先進(jìn)行解析,即將常量池中的符號引用替換成直接引用,
//此時就會觸發(fā)Student.Class?的加載
if?(METHOD->constants()->tag_at(index).is_unresolved_klass())?{
CALL_VM(InterpreterRuntime::quicken_io_cc(THREAD),?handle_exception);
}
public?class?Test?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????URL?url[]?=?new?URL[1];
????????url[0]?=?Thread.currentThread().getContextClassLoader().getResource("");
????????final?CustomClassloader?customClassloader?=?new?CustomClassloader(url);
????????Thread.currentThread().setContextClassLoader(customClassloader);
????????Class?clazz?=?customClassloader.loadClass("com.wangxiandeng.ClassTest");
????????Object?object?=?clazz.newInstance();
????????Method?method?=?clazz.getDeclaredMethod("test");
????????method.invoke(object);
????}
}
public?class?ClassTest?{
????public?void?test()?throws?Exception{
????????Class?clazz?=?Thread.currentThread().getContextClassLoader().loadClass("com.wangxiandeng.Student");
????????Student?student?=?(Student)?clazz.newInstance();
????????System.out.print(student.getClass().getClassLoader());
????}
}
四:總結(jié)
推薦閱讀:
【107期】談?wù)劽嬖嚤貑柕腏ava內(nèi)存區(qū)域(運(yùn)行時數(shù)據(jù)區(qū)域)和內(nèi)存模型(JMM)
【106期】面試官:Java中的finally一定會被執(zhí)行嗎?
【105期】面試官:注冊中心全部宕掉后,Dubbo服務(wù)還能進(jìn)行調(diào)用嗎?
微信掃描二維碼,關(guān)注我的公眾號
朕已閱?
評論
圖片
表情

