1. 程式人生 > >超詳細java中的ClassLoader詳解

超詳細java中的ClassLoader詳解

本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出

ClassLoader翻譯過來就是類載入器,普通的Java開發者其實用到的不多,但對於某些框架開發者來說卻非常常見。理解ClassLoader的載入機制,也有利於我們編寫出更高效的程式碼。ClassLoader的具體作用就是將class檔案載入到jvm虛擬機器中去,程式就可以正確運行了。但是,jvm啟動的時候,並不會一次性載入所有的class檔案,而是根據需要去動態載入。想想也是的,一次性載入那麼多jar包那麼多class,那記憶體不崩潰。本文的目的也是學習ClassLoader這種載入機制。

備註:本文篇幅比較長,但內容簡單,大家不要恐慌,安靜地耐心翻閱就是

Class檔案的認識

我們都知道在Java中程式是執行在虛擬機器中,我們平常用文字編輯器或者是IDE編寫的程式都是.java格式的檔案,這是最基礎的原始碼,但這類檔案是不能直接執行的。如我們編寫一個簡單的程式HelloWorld.java

public class HelloWorld{

    public static void main(String[] args){
        System.out.println("Hello world!");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如圖: 
這裡寫圖片描述 
然後,我們需要在命令列中進行java檔案的編譯

javac HelloWorld.java
  • 1
  • 1

這裡寫圖片描述 
可以看到目錄下生成了.class檔案

我們再從命令列中執行命令:

java HelloWorld
  • 1
  • 1

這裡寫圖片描述

上面是基本程式碼示例,是所有入門JAVA語言時都學過的東西,這裡重新拿出來是想讓大家將焦點回到class檔案上,class檔案是位元組碼格式檔案,java虛擬機器並不能直接識別我們平常編寫的.java原始檔,所以需要javac這個命令轉換成.class檔案。另外,如果用C或者Python編寫的程式正確轉換成.class檔案後,java虛擬機器也是可以識別執行的。更多資訊大家可以參考這篇

瞭解了.class檔案後,我們再來思考下,我們平常在Eclipse中編寫的java程式是如何執行的,也就是我們自己編寫的各種類是如何被載入到jvm(java虛擬機器)中去的。

你還記得java環境變數嗎?

初學java的時候,最害怕的就是下載JDK後要配置環境變量了,關鍵是當時不理解,所以戰戰兢兢地照著書籍上或者是網路上的介紹進行操作。然後下次再弄的時候,又忘記了而且是必忘。當時,心裡的想法很氣憤的,想著是–這東西一點也不人性化,為什麼非要自己配置環境變數呢?太不照顧菜鳥和新手了,很多菜鳥就是因為卡在環境變數的配置上,遭受了太多的挫敗感。

因為我是在Windows下程式設計的,所以只講Window平臺上的環境變數,主要有3個:JAVA_HOMEPATHCLASSPATH

JAVA_HOME

指的是你JDK安裝的位置,一般預設安裝在C盤,如

C:\Program Files\Java\jdk1.8.0_91
  • 1
  • 1

PATH

將程式路徑包含在PATH當中後,在命令列視窗就可以直接鍵入它的名字了,而不再需要鍵入它的全路徑,比如上面程式碼中我用的到javacjava兩個命令。 
一般的

PATH=%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;%PATH%;
  • 1
  • 1

也就是在原來的PATH路徑上新增JDK目錄下的bin目錄和jre目錄的bin.

CLASSPATH

CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar
  • 1
  • 1

一看就是指向jar包路徑。 
需要注意的是前面的.;.代表當前目錄。

環境變數的設定與檢視

設定可以右擊我的電腦,然後點選屬性,再點選高階,然後點選環境變數,具體不明白的自行查閱文件。

檢視的話可以開啟命令列視窗


echo %JAVA_HOME%

echo %PATH%

echo %CLASSPATH%
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

好了,扯遠了,知道了環境變數,特別是CLASSPATH時,我們進入今天的主題Classloader.

JAVA類載入流程

Java語言系統自帶有三個類載入器: 
Bootstrap ClassLoader 最頂層的載入類,主要載入核心類庫,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通過啟動jvm時指定-Xbootclasspath和路徑來改變Bootstrap ClassLoader的載入目錄。比如java -Xbootclasspath/a:path被指定的檔案追加到預設的bootstrap路徑中。我們可以開啟我的電腦,在上面的目錄下檢視,看看這些jar包是不是存在於這個目錄。 
Extention ClassLoader 擴充套件的類載入器,載入目錄%JRE_HOME%\lib\ext目錄下的jar包和class檔案。還可以載入-D java.ext.dirs選項指定的目錄。 
Appclass Loader也稱為SystemAppClass 載入當前應用的classpath的所有類。

我們上面簡單介紹了3個ClassLoader。說明了它們載入的路徑。並且還提到了-Xbootclasspath-D java.ext.dirs這兩個虛擬機器引數選項。

載入順序?

我們看到了系統的3個類載入器,但我們可能不知道具體哪個先行呢? 
我可以先告訴你答案 
1. Bootstrap CLassloder 
2. Extention ClassLoader 
3. AppClassLoader

為了更好的理解,我們可以檢視原始碼。 
sun.misc.Launcher,它是一個java虛擬機器的入口應用。

public class Launcher {
    private static Launcher launcher = new Launcher();
    private static String bootClassPath =
        System.getProperty("sun.boot.class.path");

    public static Launcher getLauncher() {
        return launcher;
    }

    private ClassLoader loader;

    public Launcher() {
        // Create the extension class loader
        ClassLoader extcl;
        try {
            extcl = ExtClassLoader.getExtClassLoader();
        } catch (IOException e) {
            throw new InternalError(
                "Could not create extension class loader", e);
        }

        // Now create the class loader to use to launch the application
        try {
            loader = AppClassLoader.getAppClassLoader(extcl);
        } catch (IOException e) {
            throw new InternalError(
                "Could not create application class loader", e);
        }

        //設定AppClassLoader為執行緒上下文類載入器,這個文章後面部分講解
        Thread.currentThread().setContextClassLoader(loader);
    }

    /*
     * Returns the class loader used to launch the main application.
     */
    public ClassLoader getClassLoader() {
        return loader;
    }
    /*
     * The class loader used for loading installed extensions.
     */
    static class ExtClassLoader extends URLClassLoader {}

/**
     * The class loader used for loading from java.class.path.
     * runs in a restricted security context.
     */
    static class AppClassLoader extends URLClassLoader {}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

原始碼有精簡,我們可以得到相關的資訊。 
1. Launcher初始化了ExtClassLoader和AppClassLoader。 
2. Launcher中並沒有看見BootstrapClassLoader,但通過System.getProperty("sun.boot.class.path")得到了字串bootClassPath,這個應該就是BootstrapClassLoader載入的jar包路徑。

我們可以先程式碼測試一下sun.boot.class.path是什麼內容。

System.out.println(System.getProperty("sun.boot.class.path"));
  • 1
  • 1

得到的結果是:

C:\Program Files\Java\jre1.8.0_91\lib\resources.jar;
C:\Program Files\Java\jre1.8.0_91\lib\rt.jar;
C:\Program Files\Java\jre1.8.0_91\lib\sunrsasign.jar;
C:\Program Files\Java\jre1.8.0_91\lib\jsse.jar;
C:\Program Files\Java\jre1.8.0_91\lib\jce.jar;
C:\Program Files\Java\jre1.8.0_91\lib\charsets.jar;
C:\Program Files\Java\jre1.8.0_91\lib\jfr.jar;
C:\Program Files\Java\jre1.8.0_91\classes
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

可以看到,這些全是JRE目錄下的jar包或者是class檔案。

ExtClassLoader原始碼

如果你有足夠的好奇心,你應該會對它的原始碼感興趣

/*
     * The class loader used for loading installed extensions.
     */
    static class ExtClassLoader extends URLClassLoader {

        static {
            ClassLoader.registerAsParallelCapable();
        }

        /**
         * create an ExtClassLoader. The ExtClassLoader is created
         * within a context that limits which files it can read
         */
        public static ExtClassLoader getExtClassLoader() throws IOException
        {
            final File[] dirs = getExtDirs();

            try {
                // Prior implementations of this doPrivileged() block supplied
                // aa synthesized ACC via a call to the private method
                // ExtClassLoader.getContext().

                return AccessController.doPrivileged(
                    new PrivilegedExceptionAction<ExtClassLoader>() {
                        public ExtClassLoader run() throws IOException {
                            int len = dirs.length;
                            for (int i = 0; i < len; i++) {
                                MetaIndex.registerDirectory(dirs[i]);
                            }
                            return new ExtClassLoader(dirs);
                        }
                    });
            } catch (java.security.PrivilegedActionException e) {
                throw (IOException) e.getException();
            }
        }

        private static File[] getExtDirs() {
            String s = System.getProperty("java.ext.dirs");
            File[] dirs;
            if (s != null) {
                StringTokenizer st =
                    new StringTokenizer(s, File.pathSeparator);
                int count = st.countTokens();
                dirs = new File[count];
                for (int i = 0; i < count; i++) {
                    dirs[i] = new File(st.nextToken());
                }
            } else {
                dirs = new File[0];
            }
            return dirs;
        }

......
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

我們先前的內容有說過,可以指定-D java.ext.dirs引數來新增和改變ExtClassLoader的載入路徑。這裡我們通過可以編寫測試程式碼。


            
           

相關推薦

詳細java 大發彩_票平臺搭建 的ClassLoader

his sco string 配置環境變量 javac 選項 handle getc java開發 ClassLoader翻譯過來就是類加載器,普通的java開發者其實用到的不多,但對於某些框架開發者來說卻非常常見。理解ClassLoader的加載機制,也有利於我們編寫出更

一看你就懂,詳細 java ClassLoader

繼續 inter utils 普通 原來 handle 思考 還需 ear ClassLoader翻譯過來就是類加載器,普通的java開發者其實用到的不多,但對於某些框架開發者來說卻非常常見。理解ClassLoader的加載機制,也有利於我們編寫出更高效的代碼。ClassL

詳細javaClassLoader

本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出 ClassLoader翻譯過來就是類載入器,普通的Java開發者其實用到的不多,但對於某些框架開發者來說卻非常常見。理解ClassLoader的載入機制,也有利於我們編寫出更高效的程式碼。C

詳細的Hadoop2配置

site work .data pro class In art etc 刷新 1. 集群環境Master 192.168.2.100Slave1 192.168.2.101Slave2 192.168.2.102 2. 下載安裝包#Masterwget http://mi

Oracle數據庫的安裝 【詳細的文圖

ech passwd pat 適用於 下載 當前 play 領域 .so Oracle簡介Oracle Database,又名Oracle RDBMS,或簡稱Oracle。是甲骨文公司的一款關系數據庫管理系統。它是在數據庫領域一直處於領先地位的產品。可以說Oracle數據庫

詳細Memcached+LAMP搭建操作命令

安裝位置 第一條 連接 進行 添加數據 des auth 創建 獲取數據 簡介 Memcached是一套高性能內存對象緩存系統,用於一些高負載的Web網站,主要作用是通過緩存數據庫查詢結果,減少數據庫訪問次數,以提高動態Web應用的響應速度、提高可擴展性。Memcached

javastatic

static關鍵字 1.static修飾的變數叫做“靜態變數”。 2.static修飾的方法叫做“靜態方法”。 3.static還可以定義靜態語句塊。 一下例子演示:static定義靜態語句塊 static定義的靜態語句塊在類載入的階段執行,並且只執行一次,並且是自上而下的順序執行。 p

javaimport

前言 import與package機制相關,這裡先從package入手,再講述import以及static import的作用。 package package名稱就像是我們的姓,而class名稱就像是我們的名字 。package和package的附屬關係用”.”來連線,這就像是複姓。比如說 java.

詳細!ActionBar 使用· .

一、ActionBar介紹   在Android 3.0中除了我們重點講解的Fragment外,Action Bar也是一個非常重要的互動元素,Action Bar取代了傳統的tittle bar和menu,在程式執行中一直置於頂部,對於Android平板裝置來說螢幕更大它

javavolatile

1.1 作用:它用來確保將變數的更新操作通知到其他執行緒。 volatile可以保證執行緒可見性且提供了一定的有序性,但是無法保證原子性。 1.保證可見性、不保證原子性 2.禁止指令重排序 可見性的實現: (1)修改volatile變數時會強制將修改後的值重新整

JavaCAS

在JDK 5之前Java語言是靠synchronized關鍵字保證同步的,這會導致有鎖 鎖機制存在以下問題: (1)在多執行緒競爭下,加鎖、釋放鎖會導致比較多的上下文切換和排程延時,引起效能問題。 (2)一個執行緒持有鎖會導致其它所有需要此鎖的執行緒掛起。 (3

JavaCAS(悲觀鎖與樂觀鎖)

前言:在JDK1.5之前Java語言是靠synchronized關鍵字保證同步的,這會導致有鎖鎖機制存在以下問題: (1)在多執行緒競爭下,加鎖、釋放鎖會導致比較多的上下文切換和排程延時,引起效能問題。 (2)一個執行緒持有鎖會導致其它所有需要此鎖的執行緒掛

詳細的 linux掛載

二 、linux檔案系統    檔案系統指檔案存在的物理空間,linux系統中每個分割槽都是一個檔案系統,都有自己的目錄層次結構。linux會將這些分屬不同分割槽的、單獨的檔案系統按一定的方式形成一個系統的總的目錄層次結構。一個作業系統的執行離不開對檔案的操作,因此必然要擁有並維護自己的檔案系統。     l

javaHashMap

上面方法的程式碼很簡單,但其中包含了一個非常優雅的設計:系統總是將新新增的 Entry 物件放入 table 陣列的 bucketIndex 索引處——如果 bucketIndex 索引處已經有了一個 Entry 物件,那新新增的 Entry 物件指向原有的 Entry 物件(產生一個 Entry 鏈),如果

中科院中文分詞在java呼叫(ICTCLAS2013版)

在中文分詞的時候,現在流行的有很多,下面主要介紹中科院中文分詞,現在中科院地址是http://ictclas.nlpir.org/ 首先也是開始呼叫這個介面,呼叫成功後覺得應該共享出來,讓更多人去使用。 然後主要是介紹一下怎麼用使用漢語分詞系統怎麼去呼叫。必須先在上面那個網

Java基礎之五】JavaIO

1.Java IO簡介 可能學過計算機組裝與維修的同學都知道I/O裝置,翻譯過來也就是Input/Output(輸入輸出裝置),在硬體中鍵盤、滑鼠 屬於 輸入裝置,顯示器、印表機等屬於輸出裝置,這裡輸入輸出參考物是計算機本身。 java.io包從巨集觀上來

JavaCAS,分析的通俗易懂

在JDK 5之前Java語言是靠synchronized關鍵字保證同步的,這會導致有鎖 鎖機制存在以下問題: (1)在多執行緒競爭下,加鎖、釋放鎖會導致比較多的上下文切換和排程延時,引起效能問題。 (2)一個執行緒持有鎖會導致其它所有需要此鎖的執行緒掛起。 (3)如果

詳細!ActionBar 使用·

一、ActionBar介紹   在Android 3.0中除了我們重點講解的Fragment外,Action Bar也是一個非常重要的互動元素,Action Bar取代了傳統的tittle bar和menu,在程式執行中一直置於頂部,對於Android平板裝置來

Javainstanceof

java 中的instanceof 運算子是用來在執行時指出物件是否是特定類的一個例項。instanceof通過返回一個布林值來指出,這個物件是否是這個特定類或者是它的子類的一個例項。 用法: result = obj

酷炫開源專案cardsui-for-android-詳細原始碼分析,所用特效是如何實現的

以下是我擷取的2個圖片,可以自定義成Card形式的View,佈局可以自己設定。點選露出來的部分可以使點選的Card滑落到下面,也可以左右滑動刪除Card。效果非常好        這篇文章主要寫下通過原始碼分析一下幾個地方是怎麼實現的。 Card的View和佈局 相互疊