JVM底層之ClassLoader源碼解析及自定義ClassLoader

1.JVM運行流程

JVM運行流程如下圖所示:

JVM運行流程.png

2.JVM基本結構

JVM基本機構包括:類加載器膜钓,執(zhí)行引擎欲低,運行時數(shù)據(jù)區(qū)坚洽,本地接口
Class Files-->ClassLoader-->運行時數(shù)據(jù)區(qū)-->執(zhí)行引擎(本地庫接口)-->本地方法庫
具體參考我之前的文章:jvm內(nèi)存模型概述

3.ClassLoader雙親委派模型

由上面的概念可知爷肝,ClassLoader的作用就是將字節(jié)碼文件(Class Files)加載到運行時數(shù)據(jù)區(qū)嫉嘀。

3.1類的裝載過程

加載-->連接(驗證炼邀、準備、解析)-->初始化-->使用-->卸載

說明:在此只是簡單的介紹一下類的裝載過程剪侮,詳細的過程請自行百度拭宁。

加載:取得類的字節(jié)碼文件的二進制字節(jié)流洛退,加載之后會產(chǎn)生一個類對象(Class對象:保存類的定義或者結構,放入堆中)
初始化:執(zhí)行類的構造器杰标,為類的一些靜態(tài)變量賦予正確的初始值
初始化的代碼事例如下圖所示:

類的裝載-初始化.png

注意:static塊中的變量必須在static塊之前聲明兵怯,否則只能寫,不能讀腔剂。

構造器包含以下內(nèi)容:
a.static變量(不能是final修飾的常量)
b.static塊

構造器與構造方法的區(qū)別:
a.構造器:構造Class對象
b.構造方法:實例化對象媒区,先要執(zhí)行類的構造器,才能實例化對象

3.2 ClassLoader簡介

JDK已有的ClassLoader:
Bootstrap ClassLoader(啟動加載器掸犬,必須要首先啟動JVM袜漩,通過C++實現(xiàn),存在于JVM的內(nèi)核中湾碎,用null表示):主要加載jdk lib下的rt.jar等
Extension ClassLoader (Java實現(xiàn)宙攻,繼承自ClassLoader):加載%JAVA_HOME%/lib/ext/*.jar
App ClassLoader(Java實現(xiàn),繼承自ClassLoader):加載classpath下的jar
自定義類加載器介褥,也必須繼承自ClassLoader:加載自定義路徑下的jar

3.3 雙親委派模型

類加載器都有一個父類加載器先執(zhí)行座掘,他與父類加載器不是繼承關系,而是組合關系(成員變量)柔滔。
他加載的任務必須先委托給他的父類加載器溢陪,如果父類沒有加載到這個類,則繼續(xù)由發(fā)起加載的這個類加載器加載(子類需要重寫findClass方法)廊遍,如果沒有加載到嬉愧,則報出ClassNotFoudException

注意:
a.使用雙親委派模型的原因:避免類的重復加載,不會產(chǎn)生不必要的安全隱患
b.findClass的目的就是用來實現(xiàn)自定義ClassLoader的方法(建議重寫findClass喉前,不建議重寫loadClass)

4.ClassLoader源碼解析

ClassLoader主要方法LoadClass没酣,實現(xiàn)了加載類的二進制字節(jié)流的功能

loadClass.png

繼續(xù)往下看,

image.png

紅圈序號說明如下:

  1. 先根據(jù)傳入的全限定名稱加載Class對象
  2. 如果class對象為空的情況卵迂,分別對應以下兩種情況:
    • 如果他的父類加載器不為空裕便,則由父類加載器執(zhí)行類的裝載工作
    • 如果父類加載器為空,則由Bootstrap ClassLoader執(zhí)行類的裝載工作见咒,此時其實由JVM內(nèi)核執(zhí)行加載動作偿衰,代碼如下所示:
image.png
  1. 如果class對象為空的話,則由子類的findClass實現(xiàn)類的裝載工作
image.png
image.png

一般情況下改览,實現(xiàn)自定義類加載器則需要重寫findClass方法即可下翎。

5.從源碼分析實現(xiàn)自定義類加載器

測試代碼用idea開發(fā),代碼結構如下:

代碼結構.png

a. 自定義ClassLoader類如下:

package lyx.demo.classloader;

import java.io.*;

/**
 * Created by landyChris on 2017/10/29.
 */
public class MyClassLoader extends ClassLoader {

    private String path;//加載類的路徑
    private String name;//類加載器的名稱

    //指定父類加載器
    public MyClassLoader(ClassLoader parent, String path, String name) {
        super(parent);//顯示指定父類加載器
        this.path = path;
        this.name = name;
    }

    public MyClassLoader(String path, String name) {
        super();//讓系統(tǒng)類加載器成為該類的父加載器
        this.path = path;
        this.name = name;
    }

    /**
     * 通過自定義ClassLoader加載我們自己定義的類
     * @param name 全路徑名稱宝当,類似:lyx.demo.xxx.Demo
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] data = readClassFile2ByteArray(name);
        return this.defineClass(name,data,0,data.length);
    }

    /**
     * 獲取.class字節(jié)數(shù)組
     * lyx.demo.xxx.Demo-->d:/lyx/demo/xxx/Demo.class
     * @param name
     * @return
     */
    private byte[] readClassFile2ByteArray(String name) {

        InputStream is = null;

        byte returnData[] = null;

        name = name.replaceAll("\\.", File.separator);

        String filePath = this.path + name + ".class";

        File file = new File(filePath);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {
            is = new FileInputStream(file);

            int tmp = 0;
            while ((tmp = is.read()) != -1){
                baos.write(tmp);
            }
            returnData = baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //關閉流
            try {
                if(is != null) {
                    is.close();
                }
                if(baos != null) {
                    baos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return returnData;
    }

    @Override
    public String toString() {
        return this.name;
    }
}

b. 新建一個Test類视事,用于測試用途,代碼如下:

public class Test {
  public Test() {
    System.out.println("A ClassLoader:" + this.getClass().getClassLoader()       + " from classpath");
  }
}

然后在自己的某個盤符下新建一個目錄庆揩,比如我電腦是d:/tmp

image.png

還是上面的Test類俐东,只是把打印信息改為以下語句:

System.out.println("A ClassLoader:" + this.getClass().getClassLoader() + " from customer ,path=d:/tmp");
image.png

在d:tmp目錄下編譯好后跌穗,如下圖所示:

image.png

c.編寫測試類

package lyx.demo.classloader;

/**
 * Created by landyChris on 2017/10/29.
 */
public class TestClassLoader {
    public static void main(String[] args) throws Exception{
        MyClassLoader loader = new MyClassLoader(null,"d:/tmp/","landy");
        Class<?> c = loader.loadClass("Test");
        c.newInstance();
    }
}

如上代碼,運行結果如下:

image.png

以上結果說明:ClassLoader的執(zhí)行是符合雙親委派模型的虏辫,它會先讓父類加載器(App ClassLoader)執(zhí)行l(wèi)oadClass的動作蚌吸,如果找到Test類則直接返回,此例中砌庄,在該classpath下是有Test類的羹唠,如下圖所示:

image.png

為了達到我們需要執(zhí)行的是d:/tmp目錄下的Test類,我們可以兩種做法鹤耍,一種是直接刪除idea工程中的Test類肉迫。一種是利用:啟動加載器可以用null值表示,我們可以修改以下代碼達到目的:

MyClassLoader loader = new MyClassLoader(null,"d:/tmp/","landy");

結果如下:

image.png

到此稿黄,已得到想要的結果喊衫。

事例代碼地址:https://github.com/landy8530/interview

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市杆怕,隨后出現(xiàn)的幾起案子族购,更是在濱河造成了極大的恐慌,老刑警劉巖陵珍,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寝杖,死亡現(xiàn)場離奇詭異,居然都是意外死亡互纯,警方通過查閱死者的電腦和手機瑟幕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來留潦,“玉大人只盹,你說我怎么就攤上這事⊥迷海” “怎么了殖卑?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坊萝。 經(jīng)常有香客問我孵稽,道長,這世上最難降的妖魔是什么十偶? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任菩鲜,我火速辦了婚禮,結果婚禮上惦积,老公的妹妹穿的比我還像新娘接校。我一直安慰自己,他們只是感情好荣刑,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布馅笙。 她就那樣靜靜地躺著,像睡著了一般厉亏。 火紅的嫁衣襯著肌膚如雪董习。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天爱只,我揣著相機與錄音皿淋,去河邊找鬼。 笑死恬试,一個胖子當著我的面吹牛窝趣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播训柴,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼哑舒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了幻馁?” 一聲冷哼從身側(cè)響起洗鸵,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎仗嗦,沒想到半個月后膘滨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡稀拐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年火邓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片德撬。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡铲咨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出砰逻,到底是詐尸還是另有隱情鸣驱,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布蝠咆,位于F島的核電站踊东,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏刚操。R本人自食惡果不足惜闸翅,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望菊霜。 院中可真熱鬧坚冀,春花似錦、人聲如沸鉴逞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至液南,卻和暖如春壳猜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背滑凉。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工统扳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人畅姊。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓咒钟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親若未。 傳聞我的和親對象是個殘疾皇子朱嘴,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

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