java 類加載器 雙親委派 根加載器、擴展類加載器、系統類加載器

前言

以下所述,基於java8所寫;類加載器通過完全限定名(比如,com.mypak.module1.MyClass)來加載類,它使類可以動態加載類到jvm中,java並未規定類的位置,可以來自本地文件系統,也可以來自網絡。

加載器分類

類加載器分爲根加載器(bootstrap classloader)擴展類加載器(ext classloader)系統類加載器(system classloader)自定義類加載器(通常繼承java.net.URLClassLoader,重寫findClass()),它們的關係通常如下。
在這裏插入圖片描述
從圖中可以看出,每個類加載器都有一個父加載器(加載器有引用指向父加載器,而不是繼承),在加載類時,首先check本身是否已加載,如果已加載,則返回,如果未加載,如果有父加載器則交給父加載器,如果沒有父加載器則使用根加載器,如果仍沒找到,則本加載器加載類,每一級加載器都執行相同的操作,這種機制稱爲委託機制,英語是parents delegation model,翻譯過來是雙親委派機制(其實有點詞不達意)。

  • 由於雙親委派機制,加載java.lang.String 時會一直往上委派,直到根加載器,而根加載器只會加載java_home/jre/lib/rt.jar中的java.lang.String,從而確保自定義的java.lang.String不會加載到jvm中,而不會讓jvm錯亂。
  • 類加載器+類的完全限定名,組成了在jvm中的唯一標識,如果類加載器不一樣,即使類限定名相同,也不相等。

類加載器用抽象類java.lang.ClassLoader表示,通過loadClass源碼可以看出,它是按照雙親加載的機制來執行的。

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); //首先,check自身加載器是否已加載目標類
            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); //調用findClass()方法

                    // 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;
        }
    }

根加載器

根加載器主要是用來加載java_home/jre/lib下的jar包,比如rt.jar(含有全部java api的類),根加載器用C/C++實現,用null表示,在java代碼中無法獲取到根加載器。
在這裏插入圖片描述
rt.jar如下
在這裏插入圖片描述

擴展類加載器

用來加載System.getproperty("java.ext.dirs")也就是java_home/jre/lib/ext`下的jar包,擴展類加載器的父加載器是根加載器。
在這裏插入圖片描述

系統類加載器

用來加載System.getproperty("java.class.path")也就是我們常說的classpath下的類,此路徑下都是應用程序的類,所以也可稱爲應用程序類加載器,它的父加載器是擴展類加載器,classLoader.getSystemClassLoader()返回的就是系統類加載器

自定義類加載器

在程序運行時,如需自定義類加載器,通常繼承java.net.URLClassLoader,重寫findClass方法,這樣符合雙親委派機制。
在這裏插入圖片描述

實例

系統類加載器–>擴展類加載器–>根加載器

public class MyClassLoader {
    public static void main(String[] args) {
        // 系統類加載器(應用類加載器)
        System.out.println(MyClassLoader.class.getClassLoader());

         //擴展類加載器
        System.out.println(MyClassLoader.class.getClassLoader().getParent());
        //系統類加載器
        System.out.println(MyClassLoader.class.getClassLoader().getParent().getSystemClassLoader());
        
		//根加載器
        System.out.println(MyClassLoader.class.getClassLoader().getParent().getParent());

		//擴展類加載器,java_home/jre/lib/ext/dnsns.jar
        System.out.println(DNSNameService.class.getClassLoader());

		//根加載器
        System.out.println(String.class.getClassLoader());
    }
}

輸出

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1b6d3586
sun.misc.Launcher$AppClassLoader@18b4aac2
null
sun.misc.Launcher$ExtClassLoader@1b6d3586
null

查看源碼得知,ExtClassLoader加載System.getProperty("java.ext.dirs")路徑下的類,在我的電腦上是D:\jdk1.8.0_211\jre\lib\ext;

 static class ExtClassLoader
    extends URLClassLoader
  {
	 private static File[] getExtDirs()
	    {
	      String str = System.getProperty("java.ext.dirs");
	     //省略
	    }
    }

AppClassLoader源碼得知,它加載System.getProperty("java.class.path")下的類,在我的系統上是D:\jdk1.8.0_211\jre\lib\ext;D:\jdk1.8.0_211\jre\lib\;還有應用的類,可以看到它包含了ExtClassLoader的加載路徑。

static class AppClassLoader
    extends URLClassLoader
  {
	 public static ClassLoader getAppClassLoader(final ClassLoader paramClassLoader)
	      throws IOException
	    {
	      String str = System.getProperty("java.class.path");
	 		//省略
	    }
}

類加載器+類全限定名在jvm中是類的唯一標識

public class MyClassLoader extends ClassLoader{
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String fileName = name.substring(name.lastIndexOf(".")+1) + ".class";
        InputStream is = getClass().getResourceAsStream(fileName);
        assert is != null;
        byte[] b;
        try {
            b = new byte[is.available()];
            is.read(b);
            return defineClass(name,b,0,b.length);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;


    }

    public static void main(String[] args) {
        try {
            Class<?> aClass = new MyClassLoader().findClass("com.jun.javase.MyClassLoader");
            System.out.println(aClass+"---"+MyClassLoader.class);
            System.out.println(aClass.getClassLoader()+"---"+MyClassLoader.class.getClassLoader());
            System.out.println(MyClassLoader.class == aClass);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

輸出

class com.jun.javase.MyClassLoader---class com.jun.javase.MyClassLoader
[email protected]$AppClassLoader@18b4aac2
false

輸出false,因爲類加載器不同,即使同一個類,也不相等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章