欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
深入分析ClassLoader

why?

ClassLoader,即java類(lèi)加載器,主要作用是將class加載到JVM內,同時(shí)它還要考慮class由誰(shuí)來(lái)加載。在說(shuō)java的類(lèi)加載機制之前,還是像前面的博客一樣,先說(shuō)說(shuō)為什么要知道java的類(lèi)加載機制。個(gè)人認為主要有以下幾個(gè)原因:

  • 按需加載。JVM啟動(dòng)時(shí)不能確定我要加載哪些東西,或者有些類(lèi)非常大,我只希望用到它時(shí)再加載,并非一次性加載所有的class,所以這時(shí)候了解了加載機制就可以按需加載了。
  • 類(lèi)隔離。比如web容器中部署多個(gè)應用,應用之間互相可能會(huì )有沖突,所以希望盡量隔離,這里可能就要分析各個(gè)應用加載的資源和加載順序之間的沖突,針對這些沖突再自己定些規則,讓它們能夠愉快地玩耍。
  • 資源回收。如果你不了解java是如何加載資源的,又怎么理解java是如何回收資源的?

what?

一般說(shuō)到j(luò )ava的類(lèi)加載機制,都要說(shuō)到“雙親委派模型”(其實(shí)個(gè)人很不理解為什么叫“雙親”,其實(shí)英文叫“parent”)。使用這種機制,可以避免重復加載,當父親已經(jīng)加載了該類(lèi)的時(shí)候,就沒(méi)有必要子ClassLoader再加載一次。JVM根據 類(lèi)名+包名+ClassLoader實(shí)例ID 來(lái)判定兩個(gè)類(lèi)是否相同,是否已經(jīng)加載過(guò)(所以這里可以略微擴展下,可以通過(guò)創(chuàng )建不同的classloader實(shí)例來(lái)實(shí)現類(lèi)的熱部署)。
有個(gè)圖很形象(來(lái)源見(jiàn)參考資料)。

注意:如果想形象地看到j(luò )ava的類(lèi)加載順序,可以在運行java的時(shí)候加個(gè)啟動(dòng)參數,即java –verbose

下面結合上圖來(lái)詳細介紹下java的類(lèi)加載過(guò)程。

  • BootStrapClassLoader。它是最頂層的類(lèi)加載器,是由C++編寫(xiě)而成, 已經(jīng)內嵌到JVM中了。在JVM啟動(dòng)時(shí)會(huì )初始化該ClassLoader,它主要用來(lái)讀取Java的核心類(lèi)庫JRE/lib/rt.jar中所有的class文件,這個(gè)jar文件中包含了java規范定義的所有接口及實(shí)現。
  • ExtensionClassLoader。它是用來(lái)讀取Java的一些擴展類(lèi)庫,如讀取JRE/lib/ext/*.jar中的包等(這里要注意,有些版本的是沒(méi)有ext這個(gè)目錄的)。
  • AppClassLoader。它是用來(lái)讀取CLASSPATH下指定的所有jar包或目錄的類(lèi)文件,一般情況下這個(gè)就是程序中默認的類(lèi)加載器。
  • CustomClassLoader。它是用戶(hù)自定義編寫(xiě)的,它用來(lái)讀取指定類(lèi)文件 ?;谧远x的ClassLoader可用于加載非Classpath中(如從網(wǎng)絡(luò )上下載的jar或二進(jìn)制)的jar及目錄、還可以在加載前對class文件優(yōu)一些動(dòng)作,如解密、編碼等。

很多資料和文章里說(shuō),ExtClassLoader的父類(lèi)加載器是BootStrapClassLoader,其實(shí)這里省掉了一句話(huà),容易造成很多新手(比如我)的迷惑。嚴格來(lái)說(shuō),ExtClassLoader的父類(lèi)加載器是null,只不過(guò)在默認的ClassLoader 的 loadClass 方法中,當parent為null時(shí),是交給BootStrapClassLoader來(lái)處理的,而且ExtClassLoader 沒(méi)有重寫(xiě)默認的loadClass方法,所以,ExtClassLoader也會(huì )調用BootStrapLoader類(lèi)加載器來(lái)加載,這就導致“BootStrapClassLoader具備了ExtClassLoader父類(lèi)加載器的功能”??匆幌孪旅娴拇a就很容易理解上面這一大段話(huà)了。

/** * 查看父類(lèi)加載器 */private static void test1() { ClassLoader appClassLoader = ClassLoader.getSystemClassLoader(); System.out.println('系統類(lèi)裝載器:' + appClassLoader); ClassLoader extensionClassLoader = appClassLoader.getParent(); System.out.println('系統類(lèi)裝載器的父類(lèi)加載器——擴展類(lèi)加載器:' + extensionClassLoader); ClassLoader bootstrapClassLoader = extensionClassLoader.getParent(); System.out.println('擴展類(lèi)加載器的父類(lèi)加載器——引導類(lèi)加載器:' + bootstrapClassLoader);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

可以看出ExtensionClassLoaderparentnull。

三個(gè)重要的方法

查看classloader的源碼可以發(fā)現三個(gè)重要的方法:

  • loadClass。classloader加載類(lèi)的入口,此方法負責加載指定名字的類(lèi),ClassLoader的實(shí)現方法為先從已經(jīng)加載的類(lèi)中尋找,如沒(méi)有則繼續從父ClassLoader中尋找,如仍然沒(méi)找到,則從BootstrapClassLoader中尋找,最后再調用findClass方法來(lái)尋找,如要改變類(lèi)的加載順序,則可覆蓋此方法,如加載順序相同,則可通過(guò)覆蓋findClass來(lái)做特殊的處理,例如解密、固定路徑尋找等,當通過(guò)整個(gè)尋找類(lèi)的過(guò)程仍然未獲取到Class對象時(shí),則拋出ClassNotFoundException。如類(lèi)需要resolve,則調用resolveClass進(jìn)行鏈接。
  • findClass。此方法直接拋出ClassNotFoundException,因此需要通過(guò)覆蓋loadClass或此方法來(lái)以自定義的方式加載相應的類(lèi)。
  • defineClass。此方法負責將二進(jìn)制的字節碼轉換為Class對象,這個(gè)方法對于自定義加載類(lèi)而言非常重要,如二進(jìn)制的字節碼的格式不符合JVM Class文件的格式,拋出ClassFormatError;如需要生成的類(lèi)名和二進(jìn)制字節碼中的不同,則拋出NoClassDefFoundError;如需要加載的class是受保護的、采用不同簽名的或類(lèi)名是以java.開(kāi)頭的,則拋出SecurityException;如需加載的class在此ClassLoader中已加載,則拋出LinkageError。

好,可能有人看了上面會(huì )疑惑,為什么上面說(shuō)自定義classloader是需要重寫(xiě)findClass而不是loadClass或者其他方法。這里建議查看源碼。

protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // 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); } return c; } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

可以看到,JDK已經(jīng)在loadClass方法中幫我們實(shí)現了ClassLoader搜索類(lèi)的判斷方法,當在loadClass方法中搜索不到類(lèi)時(shí),loadClass方法就會(huì )調用findClass方法來(lái)搜索類(lèi),所以我們只需重寫(xiě)該方法即可。

看完這一大串可能有人還是不理解,ok,那我們現在就動(dòng)手寫(xiě)一個(gè)自己的ClassLoader,盡量包含上面三個(gè)方法。

自定義ClassLoader

先定義一個(gè)Person接口。

public interface Person { public void say();}
  • 1
  • 2
  • 3

再定一個(gè)高富帥類(lèi)實(shí)現這個(gè)接口

public class HighRichHandsome implements Person { @Override public void say() { System.out.println('I don't care whether you are rich or not'); }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

好的,開(kāi)胃菜結束,主角來(lái)了,MyClassLoader。

import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;public class MyClassLoader extends ClassLoader{ /* * 覆蓋了父類(lèi)的findClass,實(shí)現自定義的classloader */ @Override public Class findClass(String name) { byte[] bt = loadClassData(name); return defineClass(name, bt, 0, bt.length); } private byte[] loadClassData(String className) { InputStream is = getClass().getClassLoader().getResourceAsStream( className.replace('.', '/') + '.class'); ByteArrayOutputStream byteSt = new ByteArrayOutputStream(); int len = 0; try { while ((len = is.read()) != -1) { byteSt.write(len); } } catch (IOException e) { e.printStackTrace(); } return byteSt.toByteArray(); }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

代碼很簡(jiǎn)單,不解釋了,最后在測試類(lèi)LoaderTest里寫(xiě)個(gè)測試方法。

/** * 父類(lèi)classloader * @throws Exception */private static void test2() throws Exception{ MyClassLoader loader = new MyClassLoader(); Class c = loader.loadClass('com.alibaba.classload.HighRichHandsome'); System.out.println('Loaded by :' + c.getClassLoader()); Person p = (Person) c.newInstance(); p.say(); HighRichHandsome man = (HighRichHandsome) c.newInstance(); man.say(); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

main方法中調用這個(gè)方法即可。LoaderTest默認構造函數會(huì )設置AppClassLoaderparent, 測試時(shí)執行test2()方法會(huì )發(fā)現HighRichHandsome類(lèi)是委托AppClassLoader加載的,所以AppClassLoader可以訪(fǎng)問(wèn)到,不會(huì )出錯。

但是我們再想一下,如果我們直接加載,不委托給父類(lèi)加載,會(huì )出現什么情況?

/** * 自己的classloader加載 * @throws Exception */private static void test3() throws Exception{ MyClassLoader loader = new MyClassLoader(); Class c = loader.findClass('com.alibaba.classload.HighRichHandsome'); System.out.println('Loaded by :' + c.getClassLoader()); Person p = (Person) c.newInstance(); p.say(); //注釋下面兩行則不報錯 HighRichHandsome man = (HighRichHandsome) c.newInstance(); man.say(); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

可以看到,悲劇的報錯了。根據class loader命名空間規則,每個(gè)class loader都有自己唯一的命名空間,每個(gè)class loader 只能訪(fǎng)問(wèn)自己命名空間中的類(lèi),如果一個(gè)類(lèi)是委托parent加載的,那么加載后,這個(gè)類(lèi)就類(lèi)似共享的,parent和child都可以訪(fǎng)問(wèn)到這個(gè)類(lèi),因為parent是不會(huì )委托child加載類(lèi)的,所以child加載的類(lèi)parent訪(fǎng)問(wèn)不到。簡(jiǎn)單來(lái)說(shuō),即子加載器的命名空間包含了parent加載的所有類(lèi),反過(guò)來(lái)則不成立。 本例中LoaderTest類(lèi)是AppClassLoader加載的,所以其看不見(jiàn)由MyClassLoader加載的HighRichHandsome類(lèi),但Person接口是可以訪(fǎng)問(wèn)的,所以賦給Person類(lèi)型不會(huì )出錯。

一些小的知識點(diǎn)

1.Class.forName()

相信大家都寫(xiě)過(guò)連接數據庫的例子,基本上就是加載驅動(dòng),建立連接,創(chuàng )建請求,寫(xiě)prepareStatement,關(guān)閉連接之類(lèi)的。在這里,有一段代碼:

public DbTest() { try { Class.forName('com.mysql.jdbc.Driver');// 加載驅動(dòng) conn = DriverManager.getConnection(url, 'root', '');// 建立連接 stm = conn.createStatement(); // 創(chuàng )建請求 } catch (Exception e) { e.printStackTrace(); }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

我相信大家一開(kāi)始的時(shí)候肯定都有些疑惑,就是Class.forName(“com.mysql.jdbc.Driver”),為什么加載驅動(dòng)是Class.forName,而不是ClassLoader的loadClass?為什么這么寫(xiě)就可以加載驅動(dòng)了呢?

其實(shí)Class.forName()是顯示加載類(lèi),作用是要求JVM查找并加載指定的類(lèi),也就是說(shuō)JVM會(huì )執行該類(lèi)的靜態(tài)代碼段。查看com.mysql.jdbc.Driver源碼可以發(fā)現里面有個(gè)靜態(tài)代碼塊,在加載后,類(lèi)里面的靜態(tài)代碼塊就執行(主要目的是注冊驅動(dòng),把自己注冊進(jìn)去),所以主要目的就是為了觸發(fā)這個(gè)靜態(tài)方法。

2.Web容器加載機制

其實(shí)web容器的加載機制這一點(diǎn)我說(shuō)不好,因為沒(méi)看過(guò)諸如tomcat之類(lèi)的源碼,但是根據自己使用感覺(jué)以及查了相關(guān)資料,可以簡(jiǎn)單介紹一下。一般服務(wù)器端都要求類(lèi)加載器能夠反轉委派原則,也就是先加載本地的類(lèi),如果加載不到,再到parent中加載。這個(gè)得細看,我這里只知道這個(gè)概念,所以就不說(shuō)了。JavaEE規范推薦每個(gè)模塊的類(lèi)加載器先加載本類(lèi)加載的內容,如果加載不到才回到parent類(lèi)加載器中嘗試加載。

3.重復加載與回收

一個(gè)class可以被不同的class loader重復加載,但同一個(gè)class只能被同一個(gè)class loader加載一次。見(jiàn)下列代碼:

/** * 對象只加載一次,返回true */private static void test4() { ClassLoader c1 = LoaderTest.class.getClassLoader(); LoaderTest loadtest = new LoaderTest(); ClassLoader c2 = loadtest.getClass().getClassLoader(); System.out.println('c1.equals(c2):'+c1.equals(c2));}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

類(lèi)的回收。說(shuō)到最開(kāi)始的why,其實(shí)java的回收機制我前面有篇博客說(shuō)的比較詳細,這里就插一句廢話(huà)吧,當某個(gè)classloader加載的所有類(lèi)實(shí)例化的所有對象都被回收了,則該classloader會(huì )被回收。

參考:http://blog.csdn.net/xyang81/article/details/7292380

ps:話(huà)說(shuō)csdn的markdown寫(xiě)出來(lái)的效果實(shí)在不敢恭維,小小的吐槽一下。

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
InfoQ: Java深度歷險(二)——Java類(lèi)的加載、鏈接和初始化
類(lèi)加載機制(類(lèi)加載過(guò)程和類(lèi)加載器)
一步步手動(dòng)實(shí)現熱修復
JVM真香系列:輕松理解class文件到虛擬機(下)
自定義類(lèi)加載器的編寫(xiě),及分析器原理(附源碼)
通過(guò)實(shí)例Java?ClassLoader原理
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久