持續更新JVM相關知識,敬請關注:
Java虛擬機精髓專欄?zhuanlan.zhihu.com
上一節說了下類加載器和類加載過程。這一節我們看下幾種不同的類加載器。
JVM支持的類加載器有兩類,分別是引導類加載器和自定義加載器。這里的自定義自定義加載器,不僅僅指用戶自己實現的加載器,而是泛指所有繼承ClassLoader這個抽象類的加載器。
作為JAVA程序員,起碼要知道引導加載器、擴展加載器、系統加載器這三種,除此之外,還有很多用戶自定義加載器,他們的分類關系見下圖。

JAVA內部實際的繼承關系如何呢?大家可以查看下Laucher類,會發現其中有兩個內部類,ExtClassLoader和AppClassLoader,這兩個就是擴展類加載器和系統類加載器。他們都繼承自URLClassLoader,URLClassLoader再繼承自SecureClassLoader,最終繼承ClassLoader。下面我們來看一個代碼實例:

首先我們通過ClassLoader提供的方法直接獲取系統類加載器,會發現是AppClassLoader,然后我們再通過getParent方法,獲取上層擴展類加載器,發現是ExtClassLoader,之后我們再想getParent獲取引導類加載器,發現獲取不到了,輸出了null。
下面我們通過當前類的類對象,獲取他的ClassLoader,輸出的是AppClassLoader,說明當前類是由系統類加載器加載的,并且大家注意,這個系統類加載器同之前那個內存地址是一致的,有此可見,這個系統類加載器只會被加載一次。
最后我們再看下String類的加載器,發現輸出的是null,這就說明了,String加載器是通過引導類加載器加載的。系統的核心類庫,都是使用引導類加載的。
下面我們來具體說下這幾種不同的加載器:
1、引導類加載器(Bootstrap ClassLoader)
引導類加載器是由C和C++語言實現的,集成在JVM內部,所有JAVA的核心類庫(rt.jar、resources.jar、sun.boot.class.path等)通過它來加載的。引導類加載器不繼承java.lang.ClassLoader也沒有上層加載器。他是擴展類加載器和應用類加載器的父類。Java由于安全方面的考慮,引導類加載器,只能加載java、javax、sun為開頭的java自身的類庫。
我們通過代碼雖然獲取不到引導類加載器,但是我們可以查看下可以加載哪些類庫:
import
可以看到輸出結果如下:
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/resources.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/rt.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/sunrsasign.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/jsse.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/jce.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/charsets.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/jfr.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/classes
2、擴展類加載器(Extension ClassLoader)
擴展類加載器,是JVM內部自帶的加載器,Java語言編寫,對應sun.misc.Launcher$ExtClassLoader這個內部類。間接繼承自ClassLoader類,他是通過引導類加載器進行加載的。擴展類加載器負責加載java.ext.dirs所指定目錄或jre/libb/ext中的類庫,我們自己定義的jar放到這些路徑下,就會被擴展類加載器所加載。
3、系統類加載器(System ClassLoader)
系統類加載器,也叫做應用程序加載器,也是由JAVA語言編寫的,對應sun.misc.Launcher$AppClassLoader這個內部類。同樣間接繼承ClassLoader類,它是通過擴展類加載器加載的。系統類加載器負責加載classpath路徑下或java.class.path屬性下的指定的了類庫。實際上,系統類加載器是程序中的默認加載器,我們平常所編寫的絕大不部分類,默認都是由這個加載器所加載的。這個可以看上面的代碼演示結果。代碼中,我們可以通過ClassLoader提供的getSystemClassLoader()方法獲得到這個類加載器的實例。
4、用戶自定義類加載器(User Defined ClassLoader)
在某些場合下,我們使用上述三種類加載器,無法滿足我們的使用需求,這是就需要由我們自己去自定義一些類加載器,當然,這個在一般應用場景下會比較少用,所以在這不做過多講解,之后再去詳細說。用戶可以通過繼承ClassLoader,jdk1.2以后,可以重寫findClass方法來實現自定義,這里主要編寫的邏輯是加載對應路徑的類的二進制數組,然后調用defineClass()方法去生成傳入字節數組所代表類的實例。這里面對二進制數組的特殊操作,就可以根據需求去做更改了。如果需求更加簡潔,也可以直接繼承URLClassLoader來實現。