80070583类不存在_结合JVM源码谈Java类加载器
一、前言
之前文章
加多:ClassLoader解惑?zhuanlan.zhihu.com從Java層面講解了Java類加載器的原理,這里我們結(jié)合JVM源碼在稍微深入講解下。
二、Java類加載器的委托機(jī)制
Java 類加載器使用的是委托機(jī)制,也就是一個(gè)類加載器在加載一個(gè)類時(shí)候會(huì)首先嘗試讓父類加載器來(lái)加載。那么問(wèn)題來(lái)了,為啥使用這種方式?
使用委托第一這樣可以避免重復(fù)加載,第二,考慮到安全因素,下面我們看下ClassLoader類的loadClass方法:
protected Class<?> loadClass(Stringname,boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // 首先從jvm緩存查找該類Class c = findLoadedClass(name); // (1)if (c ==null) { longt0 = System.nanoTime(); try { //然后委托給父類加載器進(jìn)行加載if (parent !=null) { c = parent.loadClass(name,false); (2)} else { //如果父類加載器為null,則委托給BootStrap加載器加載c = findBootstrapClassOrNull(name); (3)} } catch (ClassNotFoundExceptione) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c ==null) { // 若仍然沒(méi)有找到則調(diào)用findclass查找// to find the class. longt1 = System.nanoTime(); c = findClass(name); (4)// this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 -t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); //(5)} returnc; } }代碼(1)表示從 JVM 緩存查找該類,如果該類之前被加載過(guò),則直接從 JVM 緩存返回該類。
代碼(2)表示如果 JVM 緩存不存在該類,則看當(dāng)前類加載器是否有父加載器,如果有的話則委托父類加載器進(jìn)行加載,否者調(diào)用(3),委托 BootStrapClassloader 進(jìn)行加載,如果還是沒(méi)有找到,則調(diào)用當(dāng)前 Classloader 的 findclass 方法進(jìn)行查找。
代碼(4)則是從本地classloader指定路徑進(jìn)行查找,其中findClass方法在路徑找到Class文件會(huì)加載二進(jìn)制字節(jié)碼到內(nèi)存,然后后會(huì)調(diào)用native方法defineClass1解析字節(jié)碼為JVM內(nèi)部的kclass對(duì)象,然后存放到Java堆的方法區(qū)。
代碼(5)則是當(dāng)字節(jié)碼加載到內(nèi)存后進(jìn)行鏈接操作,對(duì)文件格式和字節(jié)碼驗(yàn)證,并為 static 字段分配空間并初始化,符號(hào)引用轉(zhuǎn)為直接引用,訪問(wèn)控制,方法覆蓋等,本文對(duì)這些不進(jìn)入深入探討。
三、JVM源碼之defineClass1如何解析字節(jié)碼文件
本節(jié)使用的openjdk7的源碼,JVM源碼中defineClass1的定義是在ClassLoader.c文件,其解析時(shí)序圖如下:
image.png
可知步驟(8)具體解析字節(jié)碼文件,步驟(17)添加加載的類到系統(tǒng)詞典Map里面,
void SystemDictionary::update_dictionary(int d_index, unsigned int d_hash,int p_index, unsigned int p_hash,instanceKlassHandle k,Handle class_loader,TRAPS) {// Compile_lock prevents systemDictionary updates during compilationsassert_locked_or_safepoint(Compile_lock);Symbol* name = k->name();ClassLoaderData *loader_data = class_loader_data(class_loader);{MutexLocker mu1(SystemDictionary_lock, THREAD);...// 當(dāng)前對(duì)象已經(jīng)存在了?Klass* sd_check = find_class(d_index, d_hash, name, loader_data);//不存在則添加if (sd_check == NULL) {//添加kclass到系統(tǒng)詞典dictionary()->add_klass(name, loader_data, k);notice_modification();}... }其中key使用類的包路徑+類名,類加載器兩者確定,value則為具體加載的類對(duì)應(yīng)的instanceKlassHandle對(duì)象,其中維護(hù)這kclass對(duì)象。也就是系統(tǒng)詞典里面使用類加載器和類的包路徑類名唯一確定一個(gè)類。這也驗(yàn)證了在Java中同一個(gè)類使用兩個(gè)類加載器進(jìn)行加載后,加載的兩個(gè)類是不一樣的,是不能相互賦值的。
四、JVM源碼之findLoadedClass0如何查找一個(gè)類是否被加載過(guò)了
findLoadedClass0也是在ClassLoader.c文件里面,其查找時(shí)序圖:
image.png
如上時(shí)序圖主要看 SystemDictionary的find方法:
Klass* SystemDictionary::find(Symbol* class_name,Handle class_loader,Handle protection_domain,TRAPS) {...class_loader = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));ClassLoaderData* loader_data = ClassLoaderData::class_loader_data_or_null(class_loader());...unsigned int d_hash = dictionary()->compute_hash(class_name, loader_data);int d_index = dictionary()->hash_to_index(d_hash);{... return dictionary()->find(d_index, d_hash, class_name, loader_data,protection_domain, THREAD);} }Klass* Dictionary::find(int index, unsigned int hash, Symbol* name,ClassLoaderData* loader_data, Handle protection_domain, TRAPS) {//根據(jù)類名和加載器計(jì)算對(duì)應(yīng)的kclass在map里面對(duì)應(yīng)的keyDictionaryEntry* entry = get_entry(index, hash, name, loader_data);//存在,并且驗(yàn)證通過(guò)則返回if (entry != NULL && entry->is_valid_protection_domain(protection_domain)) {return entry->klass();} else {//否者返回null,說(shuō)明不存在return NULL;} }可知在查找一個(gè)類是否已經(jīng)被加載過(guò)后,也是從系統(tǒng)詞典里面根據(jù)類名和類加載器去查找是否存在的。
五、總結(jié)
本文從JVM源碼角度分析了Java中唯一含有包路徑的類名和類加載器唯一確定了一個(gè)類,在全局系統(tǒng)詞典里面就是根據(jù)包路徑的類名和類加載器計(jì)算加載的類對(duì)應(yīng)的key的。
總結(jié)
以上是生活随笔為你收集整理的80070583类不存在_结合JVM源码谈Java类加载器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 手机900e模式如何救_苹果手机如何将显
- 下一篇: java file类包_Java中Fil