<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          JVM核心類加載器及類加載的全過(guò)程,原來(lái)是這樣的 ?

          共 16319字,需瀏覽 33分鐘

           ·

          2021-10-03 01:04

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          ?運(yùn)行環(huán)境:

          下面說(shuō)明一下我的運(yùn)行環(huán)境。我是在mac上操作的. 先找到mac的java地址. 從~/.bash_profile中可以看到

          java的home目錄是: /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home


          一. 類加載的過(guò)程

          1.1 類加載器初始化的過(guò)程

          假如現(xiàn)在有一個(gè)java類 com.lxl.jvm.Math類, 里面有一個(gè)main方法

          package?com.lxl.jvm;

          public?class?Math?{
          ????public?static?int?initData?=?666;
          ????public?static?User?user?=?new?User();

          ????public?int?compute()?{
          ????????int?a?=?1;
          ????????int?b?=?2;
          ????????int?c?=?(a?+?b)?*?10;
          ????????return?c;
          ????}

          ????public?static?void?main(String[]?args)?{
          ????????Math?math?=?new?Math();
          ????????math.compute();
          ????}
          }

          這個(gè)方法很簡(jiǎn)單, 通常我們直接執(zhí)行main方法就ok, 可以運(yùn)行程序了, 那么點(diǎn)擊運(yùn)行main方法, 整個(gè)過(guò)程是如何被加載運(yùn)行的呢? 為什么點(diǎn)擊執(zhí)行main, 就能得到結(jié)果呢?

          先來(lái)看看答題的類加載流程(宏觀流程), 如下圖:

          備注:

          1. windows上的java啟動(dòng)程序是java.exe, mac下是java

          2. c語(yǔ)言部分,我們做了解, java部門是需要掌握的部分.

          第一步:?java調(diào)用底層的jvm.dll文件創(chuàng)建java虛擬機(jī)(這一步由C++實(shí)現(xiàn)) . 這里java.exe是c++寫的代碼, 調(diào)用的jvm.dll也是c++底層的一個(gè)函數(shù). 通過(guò)調(diào)用jvm.dll文件(dll文件相當(dāng)于java的jar包), 會(huì)創(chuàng)建java虛擬機(jī). java虛擬機(jī)的啟動(dòng)都是c++程序?qū)崿F(xiàn)的.

          第二步:在啟動(dòng)虛擬機(jī)的過(guò)程中, 會(huì)創(chuàng)建一個(gè)引導(dǎo)類加載器的實(shí)例. 這個(gè)引導(dǎo)類的加載器是C語(yǔ)言實(shí)現(xiàn)的. 然后jvm虛擬機(jī)就啟動(dòng)起來(lái)了.

          第三步:?接下來(lái),C++語(yǔ)言會(huì)調(diào)用java的啟動(dòng)程序.剛剛只是創(chuàng)建了java虛擬機(jī), java虛擬機(jī)里面還有很多啟動(dòng)程序. 其中有一個(gè)程序叫做Launcher. 類全稱是sun.misc.Launcher. 通過(guò)啟動(dòng)這個(gè)java類, 會(huì)由這個(gè)類引導(dǎo)加載器加載并創(chuàng)建很多其他的類加載器. 而這些加載器才是真正啟動(dòng)并加載磁盤上的字節(jié)碼文件.

          第四步:真正的去加載本地磁盤的字節(jié)碼文件,然后啟動(dòng)執(zhí)行main方法.(這一步后面會(huì)詳細(xì)說(shuō),到底是怎么加載本地磁盤的字節(jié)碼文件的。)

          第五步:main方法執(zhí)行完畢, 引導(dǎo)類加載器會(huì)發(fā)起一個(gè)c++調(diào)用, 銷毀JVM

          以上就是啟動(dòng)一個(gè)main方法, 這個(gè)類加載的全部過(guò)程

          下面, 我們重點(diǎn)來(lái)看一下, 我們的類com.lxl.Math是怎么被加載到j(luò)ava虛擬機(jī)里面去的?  

          1.2 類加載的過(guò)程

          上面的com.lxl.jvm.Math類最終會(huì)生成clas字節(jié)碼文件. 字節(jié)碼文件是怎么被加載器加載到JVM虛擬機(jī)的呢?

          類加載有五步:加載, 驗(yàn)證, 準(zhǔn)備, 解析, 初始化. 那么這五步都是干什么的呢?我們來(lái)看一下

          我們的類在哪里呢? 在磁盤里(比如: target文件夾下的class文件), 我們先要將class類加載到內(nèi)存中. 加載到內(nèi)存區(qū)域以后, 不是簡(jiǎn)簡(jiǎn)單單的轉(zhuǎn)換成二進(jìn)制字節(jié)碼文件,他會(huì)經(jīng)過(guò)一系列的過(guò)程. 比如: 驗(yàn)證, 準(zhǔn)備, 解析, 初始化等. 把這一些列的信息轉(zhuǎn)變成內(nèi)元信息, 放到內(nèi)存里面去. 我們來(lái)看看具體的過(guò)程

          第一步: 加載.

          將class類加載到j(luò)ava虛擬機(jī)的內(nèi)存里去, 在加載到內(nèi)存之前, 會(huì)有一系列的操作。第一步是驗(yàn)證字節(jié)碼。

          第二步:驗(yàn)證

          驗(yàn)證字節(jié)碼加載是否正確, 比如:打開(kāi)一個(gè)字節(jié)碼文件。打眼一看, 感覺(jué)像是亂碼, 實(shí)際上不是的. 其實(shí),這里面每個(gè)字符串都有對(duì)應(yīng)的含義. 那么文件里面的內(nèi)容我們能不能替換呢?當(dāng)然不能, 一旦替換, 就不能執(zhí)行成功了. 所以, 第一步:驗(yàn)證, 驗(yàn)證什么呢?

          驗(yàn)證字節(jié)碼加載是否正確: 格式是否正確. 內(nèi)容是否符合java虛擬機(jī)的規(guī)范.

          第三步:準(zhǔn)備

          驗(yàn)證完了, 接下來(lái)是準(zhǔn)備. 準(zhǔn)備干什么呢? 比如我們的類Math, 他首先會(huì)給Math里的靜態(tài)變量賦值一個(gè)初始值. 比如我們Math里有兩個(gè)靜態(tài)變量

          public?static?int?initData?=?666;
          public?static?User?user?=?new?User();

          在準(zhǔn)備的過(guò)程中, 就會(huì)給這兩個(gè)變量賦初始值,?這個(gè)初始值并不是真實(shí)的值,?比如initData的初始值是0. 如果是boolean類型, 就賦值為false. 也就是說(shuō),?準(zhǔn)備階段賦的值是jvm固定的, 不是我們定義的值.如果一個(gè)final的常量, 比如public static final int name="zhangsan", 那么他在初始化的時(shí)候, 是直接賦初始值"zhangsan"的. 這里只是給靜態(tài)變量賦初始值

          第四步:解析

          接下來(lái)說(shuō)說(shuō)解析的過(guò)程. 解析的過(guò)程略微復(fù)雜,?解析是將"符號(hào)引用"轉(zhuǎn)變?yōu)橹苯右?

          什么是符號(hào)引用呢?

          比如我們的程序中的main方法. 寫法是固定的, 我們就可以將main當(dāng)成一個(gè)符號(hào). 比如上面的initData, int, static, 我們都可以將其稱之為符號(hào), java虛擬機(jī)內(nèi)部有個(gè)專業(yè)名詞,把他叫做符號(hào). 這些符號(hào)被加載到內(nèi)存里都會(huì)對(duì)應(yīng)一個(gè)地址. 將"符號(hào)引用"轉(zhuǎn)變?yōu)橹苯右? 指的就是, 將main, initData, int等這些符號(hào)轉(zhuǎn)變?yōu)閷?duì)應(yīng)的內(nèi)存地址. 這個(gè)地址就是代碼的直接引用. 根據(jù)直接引用的值,我們就可以知道代碼在什么位置.然后拿到代碼去真正的運(yùn)行.

          將符號(hào)引用轉(zhuǎn)變?yōu)?內(nèi)存地址", 這種有一個(gè)專業(yè)名詞, 叫靜態(tài)鏈接. 上面的解析過(guò)程就相當(dāng)于靜態(tài)鏈接的過(guò)程. 類加載期間,完成了符號(hào)到內(nèi)存地址的轉(zhuǎn)換. 有靜態(tài)鏈接, 那么與之對(duì)應(yīng)的還有動(dòng)態(tài)鏈接.

          什么是動(dòng)態(tài)鏈接呢?

          public?static?void?main(String[]?args)?{
          ????????Math?math?=?new?Math();
          ????????math.compute();
          ????}

          比如:上面這段代碼, 只有當(dāng)我運(yùn)行到math.compute()這句話的時(shí)候, 才回去加載compute()這個(gè)方法. 也就是說(shuō), 在加載的時(shí)候, 我不一定會(huì)把compute()這個(gè)方法解析成內(nèi)存地址. 只有當(dāng)運(yùn)行到這行代買的時(shí)候, 才會(huì)解析.

          我們來(lái)看看匯編代碼

          javap?-v?Math.class
          Classfile?/Users/luoxiaoli/Downloads/workspace/project-all/target/classes/com/lxl/jvm/Math.class
          ??Last?modified?2020-6-27;?size?777?bytes
          ??MD5?checksum?a6834302dc2bf4e93011df4c0b774158
          ??Compiled?from?"Math.java"
          public?class?com.lxl.jvm.Math
          ??minor?version:?0
          ??major?version:?52
          ??flags:?ACC_PUBLIC,?ACC_SUPER
          Constant?pool:
          ???#1?=?Methodref??????????#9.#35?????????//?java/lang/Object."":()V
          ???#2?=?Class??????????????#36????????????//?com/lxl/jvm/Math
          ???#3?=?Methodref??????????#2.#35?????????//?com/lxl/jvm/Math."":()V
          ???#4?=?Methodref??????????#2.#37?????????//?com/lxl/jvm/Math.compute:()I
          ???#5?=?Fieldref???????????#2.#38?????????//?com/lxl/jvm/Math.initData:I
          ???#6?=?Class??????????????#39????????????//?com/lxl/jvm/User
          ???#7?=?Methodref??????????#6.#35?????????//?com/lxl/jvm/User."":()V
          ???#8?=?Fieldref???????????#2.#40?????????//?com/lxl/jvm/Math.user:Lcom/lxl/jvm/User;
          ???#9?=?Class??????????????#41????????????//?java/lang/Object
          ??#10?=?Utf8???????????????initData
          ??#11?=?Utf8???????????????I
          ??#12?=?Utf8???????????????user
          ??#13?=?Utf8???????????????Lcom/lxl/jvm/User;
          ??#14?=?Utf8???????????????
          ??#15?=?Utf8???????????????()V
          ??#16?=?Utf8???????????????Code
          ??#17?=?Utf8???????????????LineNumberTable
          ??#18?=?Utf8???????????????LocalVariableTable
          ??#19?=?Utf8???????????????this
          ??#20?=?Utf8???????????????Lcom/lxl/jvm/Math;
          ??#21?=?Utf8???????????????compute
          ??#22?=?Utf8???????????????()I
          ??#23?=?Utf8???????????????a
          ??#24?=?Utf8???????????????b
          ??#25?=?Utf8???????????????c
          ??#26?=?Utf8???????????????main
          ??#27?=?Utf8???????????????([Ljava/lang/String;)V
          ??#28?=?Utf8???????????????args
          ??#29?=?Utf8???????????????[Ljava/lang/String;
          ??#30?=?Utf8???????????????math
          ??#31?=?Utf8???????????????MethodParameters
          ??#32?=?Utf8???????????????
          ??#33?=?Utf8???????????????SourceFile
          ??#34?=?Utf8???????????????Math.java
          ??#35?=?NameAndType????????#14:#15????????//?"":()V
          ??#36?=?Utf8???????????????com/lxl/jvm/Math
          ??#37?=?NameAndType????????#21:#22????????//?compute:()I
          ??#38?=?NameAndType????????#10:#11????????//?initData:I
          ??#39?=?Utf8???????????????com/lxl/jvm/User
          ??#40?=?NameAndType????????#12:#13????????//?user:Lcom/lxl/jvm/User;
          ??#41?=?Utf8???????????????java/lang/Object
          {
          ??public?static?int?initData;
          ????descriptor:?I
          ????flags:?ACC_PUBLIC,?ACC_STATIC

          ??public?static?com.lxl.jvm.User?user;
          ????descriptor:?Lcom/lxl/jvm/User;
          ????flags:?ACC_PUBLIC,?ACC_STATIC

          ??public?com.lxl.jvm.Math();
          ????descriptor:?()V
          ????flags:?ACC_PUBLIC
          ????Code:
          ??????stack=1,?locals=1,?args_size=1
          ?????????0:?aload_0
          ?????????1:?invokespecial?#1??????????????????//?Method?java/lang/Object."":()V
          ?????????4:?return
          ??????LineNumberTable:
          ????????line?3:?0
          ??????LocalVariableTable:
          ????????Start??Length??Slot??Name???Signature
          ????????????0???????5?????0??this???Lcom/lxl/jvm/Math;

          ??public?int?compute();
          ????descriptor:?()I
          ????flags:?ACC_PUBLIC
          ????Code:
          ??????stack=2,?locals=4,?args_size=1
          ?????????0:?iconst_1
          ?????????1:?istore_1
          ?????????2:?iconst_2
          ?????????3:?istore_2
          ?????????4:?iload_1
          ?????????5:?iload_2
          ?????????6:?iadd
          ?????????7:?bipush????????10
          ?????????9:?imul
          ????????10:?istore_3
          ????????11:?iload_3
          ????????12:?ireturn
          ??????LineNumberTable:
          ????????line?8:?0
          ????????line?9:?2
          ????????line?10:?4
          ????????line?11:?11
          ??????LocalVariableTable:
          ????????Start??Length??Slot??Name???Signature
          ????????????0??????13?????0??this???Lcom/lxl/jvm/Math;
          ????????????2??????11?????1?????a???I
          ????????????4???????9?????2?????b???I
          ???????????11???????2?????3?????c???I

          ??public?static?void?main(java.lang.String[]);
          ????descriptor:?([Ljava/lang/String;)V
          ????flags:?ACC_PUBLIC,?ACC_STATIC
          ????Code:
          ??????stack=2,?locals=2,?args_size=1
          ?????????0:?new???????????#2??????????????????//?class?com/lxl/jvm/Math
          ?????????3:?dup
          ?????????4:?invokespecial?#3??????????????????//?Method?"":()V
          ?????????7:?astore_1
          ?????????8:?aload_1
          ?????????9:?invokevirtual?#4??????????????????//?Method?compute:()I
          ????????12:?pop
          ????????13:?return
          ??????LineNumberTable:
          ????????line?15:?0
          ????????line?16:?8
          ????????line?17:?13
          ??????LocalVariableTable:
          ????????Start??Length??Slot??Name???Signature
          ????????????0??????14?????0??args???[Ljava/lang/String;
          ????????????8???????6?????1??math???Lcom/lxl/jvm/Math;
          ????MethodParameters:
          ??????Name???????????????????????????Flags
          ??????args

          ??static?{};
          ????descriptor:?()V
          ????flags:?ACC_STATIC
          ????Code:
          ??????stack=2,?locals=0,?args_size=0
          ?????????0:?sipush????????666
          ?????????3:?putstatic?????#5??????????????????//?Field?initData:I
          ?????????6:?new???????????#6??????????????????//?class?com/lxl/jvm/User
          ?????????9:?dup
          ????????10:?invokespecial?#7??????????????????//?Method?com/lxl/jvm/User."":()V
          ????????13:?putstatic?????#8??????????????????//?Field?user:Lcom/lxl/jvm/User;
          ????????16:?return
          ??????LineNumberTable:
          ????????line?4:?0
          ????????line?5:?6
          }
          SourceFile:?"Math.java"

          使用這個(gè)指令, 就可以查看Math的二進(jìn)制文件. 其實(shí)這個(gè)文件,就是上面那個(gè)二進(jìn)制代碼文件.

          看看這里面有什么東西?

          類的名稱, 大小,修改時(shí)間, 大版本,小版本, 訪問(wèn)修飾符等等

          ?Last?modified?2020-6-27;?size?777?bytes
          ??MD5?checksum?a6834302dc2bf4e93011df4c0b774158
          ??Compiled?from?"Math.java"
          public?class?com.lxl.jvm.Math
          ??minor?version:?0
          ??major?version:?52

          還有一個(gè)Constant pool 常量池. 這個(gè)常量池里面有很多東西. 我們重點(diǎn)看中間哪一行. 第一列表示一個(gè)常量的標(biāo)志符, 這個(gè)標(biāo)識(shí)符可能在其他地方會(huì)用到. 第二列就表示常量?jī)?nèi)容.

          Constant?pool:
          ???#1?=?Methodref??????????#9.#35?????????//?java/lang/Object."":()V
          ???#2?=?Class??????????????#36????????????//?com/lxl/jvm/Math
          ???#3?=?Methodref??????????#2.#35?????????//?com/lxl/jvm/Math."":()V
          ???#4?=?Methodref??????????#2.#37?????????//?com/lxl/jvm/Math.compute:()I
          ???#5?=?Fieldref???????????#2.#38?????????//?com/lxl/jvm/Math.initData:I
          ???#6?=?Class??????????????#39????????????//?com/lxl/jvm/User
          ???#7?=?Methodref??????????#6.#35?????????//?com/lxl/jvm/User."":()V
          ???#8?=?Fieldref???????????#2.#40?????????//?com/lxl/jvm/Math.user:Lcom/lxl/jvm/User;
          ???#9?=?Class??????????????#41????????????//?java/lang/Object
          ??#10?=?Utf8???????????????initData
          ??#11?=?Utf8???????????????I
          ??#12?=?Utf8???????????????user
          ??#13?=?Utf8???????????????Lcom/lxl/jvm/User;
          ??#14?=?Utf8???????????????
          ??#15?=?Utf8???????????????()V
          ??#16?=?Utf8???????????????Code
          ??#17?=?Utf8???????????????LineNumberTable
          ??#18?=?Utf8???????????????LocalVariableTable
          ??#19?=?Utf8???????????????this
          ??#20?=?Utf8???????????????Lcom/lxl/jvm/Math;
          ??#21?=?Utf8???????????????compute

          這些標(biāo)識(shí)符在后面都會(huì)被用到, 比如main方法

          ?public?static?void?main(java.lang.String[]);
          ????descriptor:?([Ljava/lang/String;)V
          ????flags:?ACC_PUBLIC,?ACC_STATIC
          ????Code:
          ??????stack=2,?locals=2,?args_size=1
          ?????????0:?new???????????#2??????????????????//?class?com/lxl/jvm/Math
          ?????????3:?dup
          ?????????4:?invokespecial?#3??????????????????//?Method?"":()V
          ?????????7:?astore_1
          ?????????8:?aload_1
          ?????????9:?invokevirtual?#4??????????????????//?Method?compute:()I
          ????????12:?pop
          ????????13:?return
          ??????LineNumberTable:
          ????????line?15:?0
          ????????line?16:?8
          ????????line?17:?13
          ??????LocalVariableTable:
          ????????Start??Length??Slot??Name???Signature
          ????????????0??????14?????0??args???[Ljava/lang/String;
          ????????????8???????6?????1??math???Lcom/lxl/jvm/Math;
          ????MethodParameters:
          ??????Name???????????????????????????Flags
          ??????args

          這里面就用到了#2 #3 #4 ,這都是標(biāo)識(shí)符的引用.

          第一句: new了一個(gè)Math(). 我們看看匯編怎么寫的?

          ?????????0:?new???????????#2??????????????????//?class?com/lxl/jvm/Math

          new + #2. #2是什么呢? 去常量池里看, #2代表的就是Math類

          ???#2?=?Class??????????????#36????????????//?com/lxl/jvm/Math

          這里要說(shuō)的還是math.compute()這個(gè)方法, 不是在類加載的時(shí)候就被加載到內(nèi)存中去了, 而是運(yùn)行main方法的時(shí)候, 執(zhí)行到這行代碼才被加載進(jìn)去, 這個(gè)過(guò)程叫做動(dòng)態(tài)鏈接.

          類加載的時(shí)候, 我們可以把"解析"理解為靜態(tài)加載的過(guò)程. 一般像靜態(tài)方法(例如main方法), 獲取其他不變的靜態(tài)方法會(huì)被直接加載到內(nèi)存中, 因?yàn)榭紤]到性能, 他們加載完以后就不會(huì)變了, 就直接將其轉(zhuǎn)變?yōu)樵趦?nèi)存中的代碼位置.

          而像math.compute()方法, 在加載過(guò)程中可能會(huì)變的方法(比如compute是個(gè)多態(tài),有多個(gè)實(shí)現(xiàn)), 那么在初始化加載的時(shí)候, 我們不會(huì)到他會(huì)調(diào)用誰(shuí), 只有到運(yùn)行時(shí)才能知道代碼的實(shí)現(xiàn), 所以在運(yùn)行的時(shí)候在動(dòng)態(tài)的去查詢他在內(nèi)存中的位置, 這個(gè)過(guò)程就是動(dòng)態(tài)加載

          第五步: 初始化

          對(duì)類的靜態(tài)變量初始化為指定的值. 執(zhí)行靜態(tài)代碼塊. 比如代碼

          public?static?int?initData?=?666;

          在準(zhǔn)備階段將其賦值為0, 而在初始化階段, 會(huì)將其賦值為設(shè)定的666  

          1.3 類的懶加載

          類被加載到方法區(qū)中以后,主要包含:運(yùn)行時(shí)常量池, 類型信息, 字段信息, 方法信息, 類加載器的引用, 對(duì)應(yīng)class實(shí)例的引用等信息.

          什么意思呢? 就是說(shuō), 當(dāng)一個(gè)類被加載到內(nèi)存, 這個(gè)類的常量,有常量名, 類型, 域信息等; 方法有方法名, 返回值類型, 參數(shù)類型, 方法作用域等符號(hào)信息都會(huì)被加載放入不同的區(qū)域.

          注意: 如果主類在運(yùn)行中用到其他類,會(huì)逐步加載這些類, 也就是說(shuō)懶加載. 用到的時(shí)候才加載.

          package?com.lxl.jvm;
          public?class?TestDynamicLoad?{
          ????static?{
          ????????System.out.println("********Dynamic?load?class**************");
          ????}

          ????public?static?void?main(String[]?args)?{
          ????????new?A();
          ????????System.out.println("*********load?test*****************");
          ????????B?b?=?null;?//?這里的b不會(huì)被加載,?除非new?B();
          ????}
          }

          class?A?{
          ????static?{
          ????????System.out.println("********load?A**************");
          ????}

          ????public?A(){
          ????????System.out.println("********initial?A**************");
          ????}
          }

          class?B?{
          ????static?{
          ????????System.out.println("********load?B**************");
          ????}

          ????public?B(){
          ????????System.out.println("********initial?B**************");
          ????}
          }

          這里定義了兩個(gè)類A和B, 當(dāng)使用到哪一個(gè)的時(shí)候, 那個(gè)類才會(huì)被加載, 比如:main方法中, B沒(méi)有被用到, 所以, 他不會(huì)被加載到內(nèi)存中.

          運(yùn)行結(jié)果

          ********Dynamic?load?class**************
          ********load?A**************
          ********initial?A**************
          *********load?test*****************

          我們看到A類被加載了,而B類沒(méi)有被加載,原因是B類只聲明了,沒(méi)有用到。

          總結(jié)幾點(diǎn)如下:

          1. 靜態(tài)代碼塊在構(gòu)造方法之前執(zhí)行

          2. 沒(méi)有被真正使用的類不會(huì)被加載

          二. 類加載器

          2.1 類加載器的類型

          類主要通過(guò)類加載器來(lái)加載, java里面有如下幾種類加載器

          1. 引導(dǎo)類加載器(Bootstrap ClassLoader)

          在上面類加載流程中,說(shuō)到在 [啟動(dòng)虛擬機(jī)的過(guò)程中, 會(huì)創(chuàng)建一個(gè)引導(dǎo)類加載器的實(shí)例] 這個(gè)引導(dǎo)類加載器的目的是什么呢?加載類

          引導(dǎo)類加載器主要負(fù)責(zé)加載最最核心的java類型。這些類庫(kù)位于jre目錄的lib目錄下**. 比如:rt.jar, charset.jar等,

          2. 擴(kuò)展類加載器(Ext ClassLoader)

          擴(kuò)展類加載器主要是用來(lái)加載擴(kuò)展的jar包。加載jar的目錄位于jre目錄的lib/ext擴(kuò)展目錄中的jar包

          3. 應(yīng)用程序類加載器(App CloassLoader)

          主要是用來(lái)加載用戶自己寫的類的。負(fù)責(zé)加載classPath路徑下的類包

          4. 自定義類加載器

          負(fù)責(zé)加載用戶自定義路徑下的類包

          引導(dǎo)類加載器是由C++幫我們實(shí)現(xiàn)的, 然后c++語(yǔ)言會(huì)通過(guò)一個(gè)Launcher類將擴(kuò)展類加載器(ExtClassLoader)和應(yīng)用程序類加載器(AppClassLoader)構(gòu)造出來(lái), 并且把他們之間的關(guān)系構(gòu)建好.

          2.2 案例

          案例一:測(cè)試jdk自帶的類加載器

          package?com.lxl.jvm;
          import?sun.misc.Launcher;
          import?java.net.URL;
          public?class?TestJDKClassLoader?{
          ????public?static?void?main(String[]?args)?{
          ????????/**
          ?????????*?第一個(gè):?String?是jdk自身自帶的類,位于jre/lib核心目錄下,?所以,?他的類加載器是引導(dǎo)類加載器
          ?????????*?第二個(gè):?加密類的classloader,?這是jdk擴(kuò)展包的一個(gè)類
          ?????????*?第三個(gè):?是我們當(dāng)前自己定義的類,?會(huì)被應(yīng)用類加載器加載
          ?????????*/
          ????????System.out.println(String.class.getClassLoader());????????????????System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName());
          ????????System.out.println(TestJDKClassLoader.class.getClassLoader().getClass().getName());
          ????}
          }

          我們來(lái)看這個(gè)簡(jiǎn)單的代碼, 運(yùn)行結(jié)果:

          null
          sun.misc.Launcher$ExtClassLoader
          sun.misc.Launcher$AppClassLoader

          解析:
          ?第一個(gè):?String?是jdk自身自帶的類,?所以,?他的類加載器是引導(dǎo)類加載器,引導(dǎo)類加載器是c++代碼,所以這里返回null
          ?第二個(gè):?加密類的classloader,?這是jdk擴(kuò)展包的一個(gè)類,?jdk擴(kuò)展包里面使用的是extClassLoader類加載器加載的
          ?第三個(gè):?是我們當(dāng)前自己定義的類,?會(huì)被AppClassLoader應(yīng)用程序加載器加載.

          我們看到ExtClassLoader和AppClassLoader都是Launcher類的一部分. 那Launcher類是什么東西呢?

          上面有提到, Launcher類是jvm啟動(dòng)的時(shí)候由C++調(diào)用啟動(dòng)的一個(gè)類. 這個(gè)類引導(dǎo)加載器加載并創(chuàng)建其他的類加載器。

          那么,第一個(gè)bootstrap引導(dǎo)類加載器, 那引導(dǎo)類加載器返回的為什么是null呢?

          因?yàn)閎ootstrap引導(dǎo)類加載器, 他不是java的對(duì)象, 他是c++生成的對(duì)象, 所以這里是看不到的

          案例二: BootstrapClassLoad和ExtClassLoader、AppClassLoader的關(guān)系

          如上圖,左邊是C語(yǔ)言程序代碼實(shí)現(xiàn), 右邊是java代碼實(shí)現(xiàn)。這里是跨語(yǔ)言調(diào)用,JNI實(shí)現(xiàn)了有c++向java跨語(yǔ)言調(diào)用。c語(yǔ)言調(diào)用的第一個(gè)java類是Launcher類。

          從這個(gè)圖中我們可以看出,C++調(diào)用java創(chuàng)建JVM啟動(dòng)器, 其中一個(gè)啟動(dòng)器是Launcher, 他實(shí)際是調(diào)用了sun.misc.Launcher類的getLauncher()方法. 那我們就從這個(gè)方法入手看看到底是如何運(yùn)行的?

          我們看到Lanucher.java類是在核心的rt.jar包里的,Lanucher是非常核心的一個(gè)類。

          我們看到getLauncher()類直接返回了launcher. 而launcher是一個(gè)靜態(tài)對(duì)象變量, 這是一個(gè)單例模式

          C++調(diào)用了getLauncher()-->直接返回了lanucher對(duì)象, 而launcher對(duì)象是在構(gòu)建類的時(shí)候就已經(jīng)初始化好了. 那么,初始化的時(shí)候做了哪些操作呢?接下來(lái)看看他的構(gòu)造方法.

          在構(gòu)造方法里, 首先定義了一個(gè)ExtClassLoader. 這是一個(gè)擴(kuò)展類加載器, 擴(kuò)展類加載器調(diào)用的是getExtClassLoader(). 接下來(lái)看一看getExtClassLoader這個(gè)方法做了什么?

          這是一個(gè)典型的多線程同步的寫法。

          在這里, 判斷當(dāng)前對(duì)象是否初始化過(guò), 如果沒(méi)有, 那么就創(chuàng)建一個(gè)ExtClassLoader()對(duì)象, 看看createExtClassLoader()這個(gè)方法做了什么事呢?

          doPrivileged是一個(gè)權(quán)限校驗(yàn)的操作, 我們可以先不用管, 直接看最后一句, return new Launcher.ExtClassLoader(var1). 直接new了一個(gè)ExtClassLoader, 其中參數(shù)是var1, 代表的是ext擴(kuò)展目錄下的文件.

          在ExtClassLoader(File[] var1)這個(gè)方法中, 這里第一步就是調(diào)用了父類的super構(gòu)造方法. 而ExtClassLoader繼承了誰(shuí)呢? 我們可以看到他繼承了URLClassLoader.

          而URLClassLoader是干什么用的呢? 其實(shí)聯(lián)想一下大概能夠猜數(shù)來(lái), 這里有一些文件路徑, 通過(guò)文件路徑加載class類.

          我們繼續(xù)看調(diào)用的super(parent), 我們繼續(xù)往下走, 就會(huì)看到調(diào)用了ClassLoader接口的構(gòu)造方法:

          這里設(shè)置了ExtClassLoader的parent是誰(shuí)? 注意看,我們發(fā)現(xiàn), ExtClassLoader的parent類是null.

          這就是傳遞過(guò)來(lái)的parent類加載器, 那么這里的parent類加載器為什么是null呢? 因?yàn)? ExtClassLoader的父類加載器是誰(shuí)呢? 他是Bootstrap ClassLoader. 而BootStrap ClassLoader是C++的類加載器, 我們不能直接調(diào)用它, 所以, 設(shè)置為null.

          其實(shí), ExtClassLoader在初始化階段就是調(diào)用了ExtClassLoader方法, 初始化了ExtClassLoader類

          接下來(lái),我們回到Launcher的構(gòu)造方法, 看看Launcher接下來(lái)又做了什么?

          可以看到, 接下來(lái)調(diào)了AppClassLoader的getAppClassLoader(var1), 這個(gè)方法. 需要注意一下的是var1這個(gè)參數(shù). var1是誰(shuí)呢? 向上看, 可以看到var1是ExtClassLoader.

          這是AppClassLoader, 應(yīng)用程序類加載器, 這個(gè)類是加載我們自己定義的類的類加載器. 他也是繼承自URLClassLoader.

          我們來(lái)看看getAppClassLoader(final ClassLoader var0)方法. 這個(gè)方法的參數(shù)就是上面?zhèn)鬟f過(guò)來(lái)的ExtClassLoader

          這里第一句話就是獲取當(dāng)前項(xiàng)目的class 文件路徑, 然后將其轉(zhuǎn)換為URL. 并調(diào)用了Launcher.AppClassLoader(var1x, var0), 其中var1x是class類所在的路徑集合, var0是擴(kuò)展的類加載器ExtClassLoader, 接下來(lái), 我們進(jìn)入到這個(gè)方法里看一看

          AppClassLoader直接調(diào)用了其父類的構(gòu)造方法, 參數(shù)是class類路徑集合, 和ExtClassLoader

          ?

          最后, 我們看到, 將ExtClassLoader傳遞給了parent變量. 這是定義在ClassLoader中的屬性, 而ClassLoader類是所有類加載器的父類. 因此, 我們也可以看到AppClassLoader的父類加載器是ExtClassLoader

          同時(shí), 我們也看到了, C++在啟動(dòng)JVM的時(shí)候, 調(diào)用了Launcher啟動(dòng)類, 這個(gè)啟動(dòng)類同時(shí)加載了ExtClassLoader和AppClassLoader.

          public?static?void?main(String[]?args)?{
          ????????
          ??ClassLoader?appClassLoader?=?ClassLoader.getSystemClassLoader();
          ??ClassLoader?extClassLoader?=?appClassLoader.getParent();
          ??ClassLoader?bootstrapClassLoad?=?extClassLoader.getParent();


          ??System.out.println("bootstrap?class?loader:?"?+?bootstrapClassLoad);
          ??System.out.println("ext?class?loader?"?+?extClassLoader);
          ??System.out.println("app?class?loader?"+?appClassLoader);
          }

          通過(guò)這個(gè)demo, 我們也可以看出, appClassLoader的父類是extClassLoader, extClassLoader的父類是bootstrapClassLoader

          輸出結(jié)果:

          bootstrap?class?loader:?null
          ext?class?loader?sun.misc.Launcher$ExtClassLoader@2a84aee7
          app?class?loader?sun.misc.Launcher$AppClassLoader@18b4aac2?

          通過(guò)上面的源碼分析,我們發(fā)現(xiàn)引導(dǎo)類加載器創(chuàng)建并加載了擴(kuò)展類加載器和應(yīng)用類加載器。而擴(kuò)展類加載器的父加載器是引導(dǎo)類加載器。應(yīng)用類加載器的父加載器是擴(kuò)展類加載器。這個(gè)結(jié)構(gòu),決定了后面類的加載方式,也就是雙親委派機(jī)制。



          ??作者?|??盛開(kāi)的太陽(yáng)

          來(lái)源 |??cnblogs.com/ITPower/p/15356099.html

          加鋒哥微信:?java3459??
          圍觀鋒哥朋友圈,每天推送Java干貨!

          瀏覽 37
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲黄色电影怎么 | 久久麻豆成人 | 国产精品嫩苞又嫩又紧又爽AV | 逼特逼视频最新网址 | 蜜乳一区二区三区四区五区六区 |