欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

通過實(shí)例解析Java class文件編譯加載過程

 更新時間:2020年07月25日 11:45:56   作者:AmourLee  
這篇文章主要介紹了通過實(shí)例解析Java class文件編譯加載過程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下

一、Java從編碼到執(zhí)行

首先我們來看一下Java是如何從編碼到執(zhí)行的呢? 我們有一個x.java文件通過執(zhí)行javac命令可以變成x.class文件,當(dāng)我們調(diào)用Java命令的時候class文件會被裝載到內(nèi)存中,這個過程叫做classloader。一般情況下我們自己寫代碼的時候會用到Java的類庫,所以在加載的時候也會把Java類庫相關(guān)的類也加載到內(nèi)存中。裝載完成之后會調(diào)用字節(jié)碼解釋器和JIT即時編譯器來進(jìn)行解釋和編譯,編譯完之后由執(zhí)行引擎開始執(zhí)行,執(zhí)行引擎下面對應(yīng)的就是操作系統(tǒng)硬件了。下圖是大體的流程:

Java叫做跨平臺的語言,JVM可以稱之為跨語言的平臺;

有個問題:java是解釋執(zhí)行還是編譯執(zhí)行?答:解釋和編譯是可以混合的,特別常用的代碼或則是代碼用到的次數(shù)特別多的時候,會把一個即時編譯做成本地編譯,這樣會很大程度上的提高效率。

Java虛擬機(jī)是如何做到這么多語言都可以在上面運(yùn)行,關(guān)鍵在于class文件,任何語言只要能編譯成class文件,并且符合class文件的規(guī)范你就可以放在Java虛擬機(jī)上去運(yùn)行。

二、詳解class文件的加載過程

接下來主要講的是一個class文件是怎么從硬盤上到內(nèi)存中,并開始執(zhí)行的。

類加載主要有三個過程:loading 、linking 、initializing;其中l(wèi)inking又分為三個步驟:verification 、preparation 、resolution;

1、首先Loading是什么意思呢?是把一個class問價load到內(nèi)存中去;

2、接下來是Linking分為了三小步:

  • verification 是用來校驗(yàn)加載進(jìn)來的class文件是否符合class文件標(biāo)準(zhǔn),如果不符合直接就會被拒絕了;
  • preparation 是將class文件靜態(tài)變量賦默認(rèn)值而不是初始值,例如static int i =8;這個步驟并不是將i賦值為8,而是賦值為默認(rèn)值0;
  • resolution 是把class文件常量池中用到的符號引用轉(zhuǎn)換成直接內(nèi)存地址,可以訪問到的內(nèi)容;

3、initializing 成為初始化,靜態(tài)變量在這個時候才會被賦值為初始值;

下面為類加載過程的簡化圖:

類加載器的加載過程是分成不同的層次來加載的,不同的類加載器來加載不同的class文件, Bootstrap >Extension>Application>Custom(自定義類加載器)

1、第一個類加載器的層次為:Bootstrap 稱為啟動類加載器,是Java類加載層次中最頂層的類加載器,負(fù)責(zé)加載JDK中的核心類庫。

2、第二個類加載器的層次為:Extension 是用來加載擴(kuò)展類的,主要負(fù)責(zé)加載Java的擴(kuò)展類庫,默認(rèn)加載JAVA_HOME/jre/lib/ext/目錄下的所有jar包。

3、第三個類加載器的層次為:Application又稱為系統(tǒng)類加載器,負(fù)責(zé)在JVM啟動時,加載來自在命令java中的classpath或者java.class.path系統(tǒng)屬性或者CLASSPATH操作系統(tǒng)屬性所指定的JAR類包和類路徑。

4、第三個類加載器的層次為:CustomClassLoader(自定義加載器)

package com.example.demo.classloader;

public class ClassLoaderScope {
  public static void main(String[] args) {
    System.out.println("-------------------Bootstrap加載類-------------------");
    String property = System.getProperty("sun.boot.class.path");
    String s = property.replaceAll(";", System.lineSeparator());
    System.out.println(s);

    System.out.println("-------------------Ext加載類-------------------");

    String property1 = System.getProperty("java.ext.dirs");
    String s1 = property1.replaceAll(";", System.lineSeparator());
    System.out.println(s1);

    System.out.println("-------------------App加載類-------------------");

    String property2 = System.getProperty("java.class.path");
    String s2 = property2.replaceAll(";", System.lineSeparator());
    System.out.println(s2);
  }
}
    /**輸出結(jié)果只截取了部分*/
    //E:\JDK\jdk1.8\jre\lib\resources.jar
    //E:\JDK\jdk1.8\jre\lib\rt.jar
    //E:\JDK\jdk1.8\jre\lib\sunrsasign.jar
    //E:\JDK\jdk1.8\jre\lib\jsse.jar
    //E:\JDK\jdk1.8\jre\lib\jce.jar
    //E:\JDK\jdk1.8\jre\lib\charsets.jar
    //E:\JDK\jdk1.8\jre\lib\jfr.jar
    //E:\JDK\jdk1.8\jre\classes
    //----------------------------------------------
    //E:\JDK\jdk1.8\jre\lib\ext
    //C:\Windows\Sun\Java\lib\ext
    //----------------------------------------------
    //E:\JDK\jdk1.8\jre\lib\charsets.jar
    //E:\JDK\jdk1.8\jre\lib\deploy.jar
    //E:\JDK\jdk1.8\jre\lib\ext\access-bridge-64.jar
    //E:\JDK\jdk1.8\jre\lib\ext\cldrdata.jar
    //E:\JDK\jdk1.8\jre\lib\ext\dnsns.jar
    //E:\JDK\jdk1.8\jre\lib\ext\jaccess.jar
    //E:\JDK\jdk1.8\jre\lib\ext\jfxrt.jar

特別注意一點(diǎn)這個的層級關(guān)系并沒有繼承的關(guān)系在里面,只是單單純純的語法上的繼承;

下圖為類加載的一個全過程:

用比較通俗的話來解釋這個過程,當(dāng)有一個類需要被加載時,首先要判斷這個類是否已經(jīng)被加載到內(nèi)存,判斷加載與否的過程是有順序的,如果有自己定義的類加載器,會先到custom class loader 的cache(緩存)中去找是否已經(jīng)加載,若已加載直接返回結(jié)果,否則到App的cache中查找,如果已經(jīng)存在直接返回,如果不存在,到Extension中查找,存在直接返回,不存在繼續(xù)向父加載器中尋找直到Bootstrap頂層,如果依然沒找到,那就是沒有加載器加載過這個類,需要委派對應(yīng)的加載器來加載,先看看這個類是否在自己的加載范圍內(nèi),如果是直接加載返回結(jié)果,若不是繼續(xù)向下委派,以此類推直到最下級,如果最終也沒能加載,就會直接拋異常ClassNotFoundException,這就是雙親委派模式。

理解雙親委派模式:

1、父加載器:不是類加載器的加載器,也不是類加載器的父類加載器(此處意思是沒有父類與子類之間的繼承關(guān)系)。

package com.example.demo.classloader;

/**
 * 驗(yàn)證了父加載器不是加載器的加載器
 */
public class ParentAndChild {
  public static void main(String[] args) {
    //AppClassLoader
    ClassLoader classLoader = ParentAndChild.class.getClassLoader();
    System.out.println(classLoader);

    //null 這里AppClassLoader的加載器不是ExtClassLoader 而是Bootstrap
    ClassLoader appclassLoader = ParentAndChild.class.getClassLoader().getClass().getClassLoader();
    System.out.println(appclassLoader);

    //ExtClassLoader  AppClassLoader的父加載器是ExtClassLoader
    ClassLoader parent = ParentAndChild.class.getClassLoader().getParent();
    System.out.println(parent);

    //null
    ClassLoader parentparent = ParentAndChild.class.getClassLoader().getParent().getParent();
    System.out.println(parentparent);

    //null
    ClassLoader parentparentparent = ParentAndChild.class.getClassLoader().getParent().getParent().getParent();
    System.out.println(parentparent);

    /**輸出結(jié)果*/
    //sun.misc.Launcher$AppClassLoader@18b4aac2
    //null
    //sun.misc.Launcher$ExtClassLoader@23fc625e
    //null
    //Exception in thread "main" java.lang.NullPointerException at com.example.demo.classloader.ParentAndChild.main(ParentAndChild.java:22)
  }
}

2、雙親委派:其工作原理的是,如果一個類加載器收到了類加載請求,并不會直接去加載,而是自下而上的向頂層類加載器查找是否已經(jīng)被加載了,如果被加載就不用進(jìn)行加載,如果未被加載過,則會自上而下的檢查是否屬于自己加載的范圍,如果屬于則加載,如果不屬于則向下委托,直到類被加載進(jìn)來才能叫做成功,如果加載不成功就會拋異常classnotfoundexeption,這就叫做雙親委派。

3、為什么要搞雙親委派模式?

主要是為了安全,這里可以使用反證法,如果任何類加載器都可以把class加載到內(nèi)存中,我們就可以自定義類加載器來加載Java.lang.string。在打包時可以把密碼存儲為String對象,偷偷摸摸的把密碼發(fā)送到自己的郵箱,這樣會造成安全問題。

三、自定義類加載器

package com.example.demo.classloader;

public class ClassLoaderByHand {
  public static void main(String[] args) throws ClassNotFoundException {
    Class<?> clazz = ClassLoaderByHand.class.getClassLoader().
        loadClass("com.example.demo.threaddemo.juc_002.Account");
    String name = clazz.getName();
    System.out.println(name);

  }
}

  /**
  * 輸出結(jié)果
  */
  //com.example.demo.threaddemo.juc_002.Account

代碼運(yùn)行結(jié)果可以看出,就是你要加載一個類你只要調(diào)用classLoader中的loadClass()方法就能把這個類加載到內(nèi)存中,加載完成之后會給你返回一個Class類的對象。

在硬盤上找到這個類的源碼,把它load到內(nèi)存,與此同時生成一個Class對象,上述的小程序是通過ClassLoaderByHand 找到他的加載器AppClassLoader 然后調(diào)用它的loadClass()方法,讓它幫我們把Account類加載進(jìn)來,返回一個clazz對象,使用clazz.getName()方法正常返回Account類。

什么時候我們需要自己定義去加載一個類?

熱部署時就是先把之前加載的類給干掉 ,然后使用的自定義類加載器來進(jìn)行重新加載

spring的動態(tài)代理,一個新的class 當(dāng)需要的時候就會把它load到內(nèi)存中

我們還是來看一下源碼吧,加載過程最主要的還是ClassLoader中的loaderClass()方法:

結(jié)合上面給的類加載過程的圖解一起看會更容易一些;

protected Class<?> loadClass(String name, boolean resolve)
      throws ClassNotFoundException
  {
    synchronized (getClassLoadingLock(name)) {
      /**
       * 在加載之前先調(diào)用findLoadedClass()方法查看是否已經(jīng)加載過此類
       * 若加載過 返回該對象
       * 如果未加載則返回null 進(jìn)行下一步
       */
      // First, check if the class has already been loaded
      Class<?> c = findLoadedClass(name);
      if (c == null) {
        long t0 = System.nanoTime();
        try {
          //判斷有無父加載器 如果不為空說明還未到頂層Bootstrap遞歸調(diào)用loadClass()
          if (parent != null) {
            c = parent.loadClass(name, false);
          } else {
            //如果沒有父加載器說明調(diào)用的加載器為Bootstrap Class Loader, 在此加載器內(nèi)存中查找是否已經(jīng)加載
            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();
          //調(diào)用自己的findClass()
          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;
    }
  }

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • spring retry 配置及使用教程

    spring retry 配置及使用教程

    這篇文章主要介紹了spring retry 配置及使用教程,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2024-01-01
  • Java異或運(yùn)算應(yīng)用場景詳解

    Java異或運(yùn)算應(yīng)用場景詳解

    這篇文章主要給大家介紹了關(guān)于Java異或運(yùn)算應(yīng)用場景的相關(guān)資料,異或運(yùn)算會應(yīng)用在很多算法題中,這里整理了幾個最常見的應(yīng)用場景,文中通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • 如何利用SpringBoot搭建WebService服務(wù)接口

    如何利用SpringBoot搭建WebService服務(wù)接口

    之前項(xiàng)目經(jīng)理想要開發(fā)一個webservice的協(xié)議,給我一個星期的時間,后面用springboot開發(fā)了webservice,這篇文章主要給大家介紹了關(guān)于如何利用SpringBoot搭建WebService服務(wù)接口的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • 日歷顯示讀出輸入的年月的java代碼

    日歷顯示讀出輸入的年月的java代碼

    這篇文章主要介紹了日歷顯示讀出輸入的年月的java代碼,有需要的朋友可以參考一下
    2013-12-12
  • 使用springboot的jar包能夠以service方式啟動

    使用springboot的jar包能夠以service方式啟動

    這篇文章主要介紹了使用springboot的jar包能夠以service方式啟動,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Spring boot整合mybatis實(shí)現(xiàn)過程圖解

    Spring boot整合mybatis實(shí)現(xiàn)過程圖解

    這篇文章主要介紹了Spring boot整合mybatis實(shí)現(xiàn)過程圖解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-08-08
  • 教你如何使用Java輸出各種形狀

    教你如何使用Java輸出各種形狀

    本文小編將向大家介紹的是如何利用Java輸出各種不同的形狀,本文一共介紹了七種有趣的形狀,感興趣的小伙伴趕快收藏起來吧
    2021-09-09
  • 基于FeignException$InternalServerError的解決方案

    基于FeignException$InternalServerError的解決方案

    這篇文章主要介紹了FeignException$InternalServerError的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • spring boot metrics監(jiān)控指標(biāo)使用教程

    spring boot metrics監(jiān)控指標(biāo)使用教程

    這篇文章主要為大家介紹了針對應(yīng)用監(jiān)控指標(biāo)暴露spring boot metrics監(jiān)控指標(biāo)的使用教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-02-02
  • java獲取文件大小的幾種方法

    java獲取文件大小的幾種方法

    這篇文章主要介紹了java獲取文件大小的幾種方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10

最新評論