Java類加載機(jī)制相關(guān)概念

1侣滩、JVM組成結(jié)構(gòu)

籠統(tǒng)而言,編寫的代碼在javac編譯器作用下,會(huì)將XXX.java代碼文件生成對(duì)應(yīng)的XXX.class字節(jié)碼文件,jvm運(yùn)行過程中解釋器逐條將字節(jié)碼解釋為機(jī)器碼來執(zhí)行司倚。(拓展:jvm Just In Time: 采用 及時(shí)編譯器 技術(shù)優(yōu)化性能,關(guān)于相關(guān)了解可見 Java-即時(shí)編譯器-原理-實(shí)踐-美團(tuán)技術(shù)團(tuán)隊(duì) )篓像。JVM結(jié)構(gòu)圖如下:

JVM.png

2动知、類裝載子系統(tǒng)(ClassLoader)

ClassLoader種類及雙親委派模型的加載機(jī)制如下圖:

ClassLoader.png

  • 為什么使用雙親委派模型?
    避免多份同樣字節(jié)碼的加載员辩,內(nèi)存是寶貴的盒粮,沒必要保存兩份相同的 Class 對(duì)象;

  • 類的加載方式:
    隱式加載:new
    顯式加載:ClassLoader 的 loadClass() 方法奠滑、Class 的forName() 方法等丹皱。

  • 類的裝載過程:

    • 1妒穴、加載
      通過 ClassLoader 的 loadClass() 方法把 class 文件字節(jié)碼加載到內(nèi)存中,并將這些靜態(tài)數(shù)據(jù)轉(zhuǎn)換成運(yùn)行時(shí)數(shù)據(jù)區(qū)方法區(qū)的類型數(shù)據(jù)摊崭,在運(yùn)行時(shí)讼油,在數(shù)據(jù)區(qū)堆中生成一個(gè)代表這個(gè)類的 java.lang.Class 對(duì)象,作為方法區(qū)類數(shù)據(jù)的訪問入口呢簸。
    • 2矮台、鏈接,分為校驗(yàn)阔墩、準(zhǔn)備嘿架、解析三步:
      • 校驗(yàn):檢查加載的 class 的正確性和安全性;
      • 準(zhǔn)備:為類變量分配存儲(chǔ)空間并設(shè)置類變量初始值啸箫;
      • 解析:JVM 將常量池內(nèi)的符號(hào)引用轉(zhuǎn)換為直接引用耸彪。(可參考:符號(hào)引用和直接引用,解析和分派)
    • 3忘苛、初始化
      執(zhí)行類變量賦值和靜態(tài)代碼塊蝉娜。
  • loadClass 和 forName 的區(qū)別?
    Class 的 forName() 得到的 class 是已經(jīng)初始化完成的扎唾。
    ClassLoader 的 loadClass() 得到的 class 是還沒有鏈接的召川。

      //Class.forName() 指明了initialize
      @CallerSensitive
      public static Class<?> forName(String className)
                  throws ClassNotFoundException {
          Class<?> caller = Reflection.getCallerClass();
          return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
      }
    
      private static native Class<?> forName0(String name, boolean initialize,
                                              ClassLoader loader,
                                              Class<?> caller)  
                                              throws ClassNotFoundException;
    
      //classLoader.loadClass()
      public Class<?> loadClass(String name) throws ClassNotFoundException {
          return loadClass(name, false);
      }
      //resolveClass(c) Links the specified class. 
      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
                      PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                      PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                      PerfCounter.getFindClasses().increment();
                  }
              }
              if (resolve) {
                  resolveClass(c);
              }
              return c;
          }
      }
    

    應(yīng)用案例:
    使用 Class.forName(“com.mysql.jdbc.Driver”) 創(chuàng)建數(shù)據(jù)庫驅(qū)動(dòng),Driver 類中是有一段靜態(tài)的代碼段的胸遇,因此需要調(diào)用 forName 才能執(zhí)行它荧呐,進(jìn)而生成 Driver 對(duì)象。
    Spring IOC 為了加快初始化速度纸镊,大量使用類延時(shí)加載技術(shù)倍阐,使用 ClassLoader 不需要執(zhí)行鏈接和初始化步驟,這樣可以加快加載速度逗威,把類的初始化工作留到實(shí)際使用到這個(gè)類的時(shí)候才去做峰搪。

3、類加載機(jī)制

Class 文件是一組以 8 位字節(jié)為基礎(chǔ)單位的二進(jìn)制流凯旭,當(dāng)遇到需要占用 8 個(gè)字節(jié)以上空間的數(shù)據(jù)項(xiàng)時(shí)概耻,則會(huì)按照高位在前的方式分割成若干個(gè) 8 位字節(jié)進(jìn)行存儲(chǔ)。
類的生命周期:加載 > 驗(yàn)證 > 準(zhǔn)備 > 解析 > 初始化 > 使用 > 卸載
加載罐呼、驗(yàn)證鞠柄、準(zhǔn)備、解析弄贿、卸載這 5 個(gè)的順序是確定的春锋,而解析則不一定,在某些情況是 初始化 > 解析差凹,這是為了支持 Java 的運(yùn)行時(shí)綁定期奔。(關(guān)于靜態(tài)綁定侧馅,動(dòng)態(tài)綁定的基礎(chǔ)知識(shí)可以參照Java單分派和雙分派以及訪問者模式的使用)
類加載過程包括加載、驗(yàn)證呐萌、準(zhǔn)備馁痴、解析、初始化 5 個(gè)階段肺孤。VM 設(shè)計(jì)團(tuán)隊(duì)把類加載階段中的"通過一個(gè)類的全限定名來獲取描述此類的二進(jìn)制字節(jié)流"這個(gè)動(dòng)作放到 JVM 外部去實(shí)現(xiàn)罗晕,以便讓應(yīng)用程序自己決定如何去獲取所需要的類。實(shí)現(xiàn)這個(gè)動(dòng)作的代碼模塊叫做"類加載器"赠堵。

  • 1.加載
    • 通過一個(gè)類的全限定名來獲取定義此類的二進(jìn)制流小渊;
    • 將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)換為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu);
    • 在內(nèi)存中生成一個(gè)代表這個(gè)類的 java.lang.Class 對(duì)象茫叭,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口酬屉。
  • 2.驗(yàn)證
    • 如果驗(yàn)證到輸入的字節(jié)流不符合 Class 文件格式的約束,VM 就應(yīng)拋出一個(gè) java.lang.VerifyError 異匙岢睿或其子類異常呐萨。驗(yàn)證階段大致會(huì)有文件格式驗(yàn)證、元數(shù)據(jù)驗(yàn)證莽囤、字節(jié)碼驗(yàn)證谬擦、符號(hào)引用驗(yàn)證 4 個(gè)階段。
  • 3.準(zhǔn)備
    • 準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值的階段朽缎,這些變量所使用的內(nèi)存都將在方法區(qū)中進(jìn)行分配惨远。這個(gè)階段中有兩個(gè)容易產(chǎn)生混淆的概念需要強(qiáng)調(diào)一下:
      • 這時(shí)候進(jìn)行內(nèi)存分配的僅包含類變量(被 static 修飾的變量),而不包含實(shí)例變量话肖,實(shí)例變量將會(huì)在對(duì)象實(shí)例化時(shí)隨著對(duì)象一起分配在 Java 堆中锨络;
      • 這里所說的初始值"通常情況"下是數(shù)據(jù)類型的零值,假設(shè)一個(gè)類變量的定義為 public static int value = 123; 那變量 value 在準(zhǔn)備階段過后的初始值是0而不是123狼牺,因?yàn)檫@個(gè)時(shí)候尚未開始執(zhí)行任何 Java 方法,value 賦值為 123 的動(dòng)作將在初始化階段才會(huì)執(zhí)行礼患。 還有一種情況是定義為 public static final int value = 123; 這種情況在準(zhǔn)備階段 VM 就會(huì)將 value 賦值為 123是钥。
  • 4.解析
    • 解析過程是 VM 將常量池中的符號(hào)引用替換為直接引用的過程。直接引用可以是直接指向目標(biāo)的指針缅叠、相對(duì)偏移量或是一個(gè)能間接定位到目標(biāo)的句柄悄泥。直接引用是和 VM 實(shí)現(xiàn)的內(nèi)存布局相關(guān)的,同一個(gè)符號(hào)引用在不同 VM 實(shí)例上翻譯出來的直接引用一般不會(huì)相同肤粱。如果有了直接引用弹囚,那引用的目標(biāo)必定已經(jīng)在內(nèi)存中存在。
  • 5.初始化
    初始化是類加載過程的最后一步领曼。對(duì)于初始化階段鸥鹉,VM 規(guī)范嚴(yán)格規(guī)定了有且只有 5 種情況必須立即對(duì)類進(jìn)行初始化:
    • 遇到 new蛮穿、getstatic、putstatic毁渗、invokestatic 這 4 個(gè)字節(jié)碼指令時(shí)践磅,場(chǎng)景有使用 new 實(shí)例化對(duì)象的時(shí)候;讀取或設(shè)置一個(gè)類的靜態(tài)字段時(shí)(被 final 修飾灸异,已在編譯期把結(jié)果放入常量池的靜態(tài)字段除外)的時(shí)候府适;調(diào)用一個(gè)類的靜態(tài)方法的時(shí)候;
    • 使用 java.lang.reflect 包的方法對(duì)類進(jìn)行反射調(diào)用的時(shí)候肺樟;
    • 當(dāng)初始化一個(gè)類的時(shí)候檐春,如果發(fā)現(xiàn)其父類還沒有進(jìn)行過初始化,則需要先觸發(fā)其父類的初始化么伯;
    • 當(dāng) VM 啟動(dòng)的時(shí)候疟暖,用戶需要指定一個(gè)要執(zhí)行的主類(包含 main 方法),VM 會(huì)先初始化這個(gè)主類蹦狂;
    • 當(dāng)使用 JDK1.7 的動(dòng)態(tài)語言支持時(shí)誓篱。

4、個(gè)人觀點(diǎn)

JAVA 基礎(chǔ)本身是一個(gè)龐大的知識(shí)體系凯楔,往往一個(gè)疑問點(diǎn)會(huì)牽涉出很多知識(shí)點(diǎn)窜骄,希望大家沉下心來,不僅僅是只看個(gè)結(jié)論摆屯。在解答疑問的查閱過程中會(huì)反復(fù)碰到一些知識(shí)點(diǎn)邻遏,多讀多看就會(huì)加深印象,得到提高虐骑。

5准验、文檔參考

JVM 技術(shù)內(nèi)幕——HotSpot VM 類加載機(jī)制
Java-即時(shí)編譯器-原理-實(shí)踐-美團(tuán)技術(shù)團(tuán)隊(duì)
符號(hào)引用和直接引用,解析和分派
Java單分派和雙分派以及訪問者模式的使用

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末廷没,一起剝皮案震驚了整個(gè)濱河市糊饱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌颠黎,老刑警劉巖另锋,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異狭归,居然都是意外死亡夭坪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門过椎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來室梅,“玉大人,你說我怎么就攤上這事⊥鍪螅” “怎么了赏殃?”我有些...
    開封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拆宛。 經(jīng)常有香客問我嗓奢,道長,這世上最難降的妖魔是什么浑厚? 我笑而不...
    開封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任股耽,我火速辦了婚禮,結(jié)果婚禮上钳幅,老公的妹妹穿的比我還像新娘物蝙。我一直安慰自己,他們只是感情好敢艰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開白布诬乞。 她就那樣靜靜地躺著,像睡著了一般钠导。 火紅的嫁衣襯著肌膚如雪震嫉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天牡属,我揣著相機(jī)與錄音票堵,去河邊找鬼。 笑死逮栅,一個(gè)胖子當(dāng)著我的面吹牛悴势,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播措伐,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼特纤,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了侥加?” 一聲冷哼從身側(cè)響起捧存,我...
    開封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎担败,沒想到半個(gè)月后矗蕊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氢架,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了朋魔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片岖研。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出孙援,到底是詐尸還是另有隱情害淤,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布拓售,位于F島的核電站窥摄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏础淤。R本人自食惡果不足惜崭放,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鸽凶。 院中可真熱鬧币砂,春花似錦、人聲如沸玻侥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凑兰。三九已至掌桩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間姑食,已是汗流浹背波岛。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留矢门,地道東北人盆色。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像祟剔,于是被迫代替她去往敵國和親隔躲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

推薦閱讀更多精彩內(nèi)容