Java类加载机制的理解
算上大學,盡管接觸Java已經(jīng)有4年時間并對基本的API算得上熟練應用,但是依舊覺得自己對于Java的特性依然是一知半解。要成為優(yōu)秀的Java開發(fā)人員,需要深入了解Java平臺的工作方式,其中類加載機制和JVM字節(jié)碼這樣的核心特性。今天我將記錄一下我在新的學習路程中對Java類加載機制的理解。
1.類加載機制
類加載是一個將類合并到正在運行著的JVM進程中的過程。首先要加載一個類,我們必須先得將類文件加載進來并連接,并且要加上大量的驗證,隨后會生成一個代表著這個類的class對象,然后就可以通過它去創(chuàng)建新的實例了。這就是我所理解的Java的類加載機制。
經(jīng)過加載和連接后出來的class對象,說明這個類已經(jīng)被加載到了JVM中,此后就不會再加載了。
2.類加載器和雙親委派模型
Java平臺里有幾個經(jīng)典的類加載器,它們分別在平臺的啟動和常規(guī)操作中承擔不同的任務:
??? 根類加載器(Bootstrap ClassLoader)——通常在虛擬機啟動不久后實例化,我們可以將其視作JVM的一部分,他的作用通常是負責加載系統(tǒng)的基礎jar包(主要是rt.jar),并且它不做驗證工作。我們開發(fā)者無法直接使用該加載器
??? 擴展類加載器(Extension ClassLoader)——用來加載安裝時自帶的標準擴展。一般包括安全性擴展。我們開發(fā)者可以直接使用。
??? 應用(或系統(tǒng))類加載器(System ClassLoader)——這是應用最廣泛的類加載器。它負責加載應用類。
??? 定制類加載器(Custom ClassLoader)——在更復雜的環(huán)境中,我們開發(fā)者可以通過繼承java.lang.ClassLoader類的方式實現(xiàn)自己的類加載器以滿足一些特殊的需求。比如說Spring框架里的封裝了自己的類加載器(我會在隨后的spring源碼學習中遇到的吧)
??? 線程上下文類加載器(Context ClassLoader)——默認為系統(tǒng)類加載器,可通過Thread.currentThread().setContextClassLoader(ClassLoader)來設置,每個線程都可以將線程上下文類加載器預先設置為父線程的類加載器。這個主要用于打破雙親委派模型,容許父類加載器通過子類加載器加載所需要的類庫。
說到雙親委派模型,我們可以通過這個圖可知:
如果說一個類加載器收到類加載請求,它并不會馬上去找,它會先把這個請求委托給他的父類加載器去完成,只有它的父類加載器反饋說找不到了,它才會自己去找(注:父類加載器它們的默認的目錄路徑都是不一樣的,一個類在虛擬機里面是用它的全限定類名+它的類加載器來確立它的唯一性),采用雙親委派的好處可以保證系統(tǒng)中的類不混亂,如你自己寫了一個java.lang.object類,并且路徑也放在lib下面,此時編譯后會有兩個object類了,采用雙親委派就只會加載rt.jar里面的object而不加載你自己寫的。
3.加載類
手動加載類有兩種方式,Class.forName()和ClassLoader.loadClass()兩種:
我們從源碼來看看他們的區(qū)別
Class.forName()
它有兩個重載方法
??? <pre name="code" class="java">??? public static Class<?> forName(String className)
??????????????????? throws ClassNotFoundException {
??????????? return forName0(className, true, ClassLoader.getCallerClassLoader());
??????? }
??? public static Class<?> forName(String name, boolean initialize,
?????????????????????????????????? ClassLoader loader)
??????? throws ClassNotFoundException
??? {
??????? if (loader == null) {
??????????? SecurityManager sm = System.getSecurityManager();
??????????? if (sm != null) {
??????????????? ClassLoader ccl = ClassLoader.getCallerClassLoader();
??????????????? if (ccl != null) {
??????????????????? sm.checkPermission(
??????????????????????? SecurityConstants.GET_CLASSLOADER_PERMISSION);
??????????????? }
??????????? }
??????? }
??????? return forName0(name, initialize, loader);
??? }
??????? /** Called after security checks have been made. */
??????? private static native Class<?> forName0(String name, boolean initialize,
??????????????????????????????????????????????? ClassLoader loader)
??????????? throws ClassNotFoundException;
第一個方法默認初始化類。
第二個方法可以選擇是否初始化類和可以選擇類加載器。
它們倆最終都是返回forName0,而forName0有native關鍵字,原生態(tài)的方法,是其他語言實現(xiàn)的,我就不深究下去了。
ClassLoader.loadClass()
它同樣也有兩個重載方法
??????? public Class<?> loadClass(String name) throws ClassNotFoundException {
??????????? return loadClass(name, false);
??????? }
??????? 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;
??????????? }
??????? }
第一個方法的具體實現(xiàn)是第二個方法。resolve參數(shù)為false。
第二個方法先檢查這個類是否被加載過,如果沒有被加載過,那就通過雙親委派的模式去找,如果父類加載器不是根類加載器,那就遞歸調(diào)用,如果是,那就試著返回根類加載器所加載的類或者返回null,當虛擬機想要裝入的不僅包括指定的類似,resolve參數(shù)為true,裝入該類英語的所有其他類。這個方法還用了其他的類,它們基本都是用native修飾的,在這也不深究,知道是用來干嘛的就好了。
以上就是我所學習的關于Java類加載器的內(nèi)容。
https://blog.csdn.net/donggua3694857/article/details/51932630
轉(zhuǎn)載于:https://www.cnblogs.com/ldq2016/p/10308653.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結
以上是生活随笔為你收集整理的Java类加载机制的理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux aria2磁力链接,Linu
- 下一篇: Java认证与授权服务JAAS基础概念