1. 程式人生 > >虛擬機器載入機制之類載入器

虛擬機器載入機制之類載入器

文章目錄


在類的載入過程中的載入階段,我們使用到了類載入器

一、在虛擬機器中,類的唯一性是怎樣確保的。

在虛擬機器中,是通過載入類的類載入器來唯一確定一個類的。所以就算連個不同的類載入器載入的class檔案一模一樣,這兩個類在虛擬機器中也不是同一個類。那麼用instanceof、equeals()方法、isAssignableFrom()、isInstance()方法也得不到想要的結果。

例:

package
fengli; import java.io.IOException; import java.io.InputStream; public class Main { public static void main(String[] args) throws Exception { ClassLoader cl = new ClassLoader() { @Override public Class<?> loadClass(String name) throws ClassNotFoundException {
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class"; InputStream is = getClass().getResourceAsStream(fileName); if (is == null) { return super.loadClass(name); } try { byte
[] b = new byte[is.available()]; is.read(b); return defineClass(name, b, 0, b.length); } catch (IOException e) { e.printStackTrace(); throw new ClassNotFoundException(name); } } }; Object obj1=cl.loadClass("fengli.Main").newInstance(); Object obj2=new Main(); System.out.println(obj1.getClass());//class fengli.Main System.out.println(obj2.getClass());//class fengli.Main System.out.println(obj1 instanceof fengli.Main);//false System.out.println(obj2 instanceof fengli.Main);//true } }

執行結果:
在這裡插入圖片描述

二、雙親委派模型

從虛擬機器角度,只存在兩種不同的類載入器。

  1. 一種是啟動類載入器(BootStrap ClassLoader),這個類載入器使用C++實現,是虛擬機器的一部分。
  2. 另一個就是其他類載入器。都繼承與java.lang.ClassLoader。這是有java語言實現的,獨立於虛擬機器外部。

然而從開發人員角度,有三種類載入器。至於這三類載入器,我在:類的載入機制具體說道它們的區別了。

  1. 啟動類載入器
  2. 擴充套件類載入器
  3. 應用程式類載入器,也稱為系統類載入器。

雙親委派的類載入器之間的層次圖。他們不是以繼承關係組織的,而是用組合關係來複用父類載入器。
在這裡插入圖片描述
雙親委派的載入過程是這樣的:

當用一個類載入器區載入Class檔案時,先讓其父類載入器區載入這個Class檔案,當其父類不能載入這個Class檔案時(在搜尋範圍沒有找到這個類),才讓這個類區載入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 {
                	//如果沒有,就嘗試使用父類載入器區載入這個類(name)
                    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
                }
				//如果父類載入器在搜尋範圍內沒有對應的類,就呼叫自己的findClass方法進行載入。
                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
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

使用雙親委派的好處是類隨著它的類載入器一同具備了層次關係。因此Object類在雙親委派下,始終是同一個類。

三、破壞雙親委派模型

雙親委派並不是一種強制性的約束模型,而是java設計者推薦給開發者的一種類載入器實現方式。在歷史上,雙親委派模型發生過三次大規模“被破壞”(並不具有貶義,指的是一種創新)情況。

第一次:添加了一個新的protected的finaClass().

第二次:是由於雙親委派的缺陷造成的,父類載入器不能呼叫子類載入器。於是使用執行緒上下文載入器區解決這個問題。

第三次:是由於使用者對程式的動態性(程式碼熱替換、模組熱部署)的追求造成的。