Java虚拟机学习 - 类加载器(ClassLoader)
類加載器
類加載器(ClassLoader)用來加載 class字節碼到 Java 虛擬機中。一般來說,Java 虛擬機使用 Java 類的方式如下:Java 源文件在經過 Javac之后就被轉換成 Java 字節碼文件(.class 文件)。類加載器負責讀取 Java 字節代碼,并轉換成 java.lang.Class 類的一個實例。每一個這樣的實例用來表示一個 Java 類。實際的情況可能更加復雜,比如 Java 字節代碼可能是通過工具動態生成的,也可能是通過網絡下載。
類與類加載器
類加載器雖然只用于實現類的加載動作,但它在Java程序中起到的作用卻遠遠不限于類加載階段。對于任意一個類,都需要由加載它的類加載器和這個類本身一同確立其在Java虛擬中的唯一性。說通俗一些,比較兩個類是否“相等”,只有在兩個類是由同一個類加載器的前提之下才有意義,否則,即使這兩個類來源于同一個class文件,只要加載它的類加載器不同,那這兩個類必定不相等。這里所指的“相等”包括代表類的Class對象的equal方法、isAssignableFrom()、isInstance()方法及instance關鍵字返回的結果。
類加載器分類:
主要分為Bootstrap?ClassLoader、Extension ClassLoader、Application ClassLoader和User Defined ClassLoader。
啟動類加載器(Bootstrap ClassLoader):
這個類加載器使用C++語言實現,并非ClassLoader的子類。主要負責加載存放在JAVA_HOME / ?jre / ?lib / rt.jar里面所有的class文件,或者被-Xbootclasspath參數所指定路徑中以rt.jar命名的文件。
擴展類加載器(Extension ClassLoader):
這個加載器由sun.misc.Launcher$ExtClassLoader實現,它負責加載AVA_HOME / ?lib / ext目錄中的,或者被java.ext.dirs系統變量所指定的路徑中的所有類庫。
應用程序類加載器(Application ClassLoader):
這個加載器由sun.misc.Launcher$AppClassLoader實現,它負責加載classpath對應的jar及目錄。一般情況下這個就是程序中默認的類加載器。
自定義類加載器(User Defined ClassLoader):
開發人員繼承ClassLoader抽象類自行實現的類加載器,基于自行開發的ClassLoader可用于并非加載classpath中(例如從網絡上下載的jar或二進制字節碼)、還可以在加載class文件之前做些小動作 如:加密等。
雙親委托模型:
上圖中所展示的類加載器之間的這種層次關系,就稱為類加載器的雙親委托模型。雙親委托模型要求除了頂層的啟動類加載器外,其余的類加載器都應當有自己的父類加載器。這里類加載器之間的父子關系一般不會以繼承的關系來實現,而是使用組合關系來復用父加載器的代碼。
public abstract class ClassLoader {private static native void registerNatives();static {registerNatives();}// The parent class loader for delegationprivate ClassLoader parent;// Hashtable that maps packages to certsprivate Hashtable package2certs = new Hashtable(11); }雙親委托的工作過程:如果一個類加載器收到了一個類加載請求,它首先不會自己去加載這個類,而是把這個請求委托給父類加載器去完成,每一個層次的類加載器都是如此,因此所有的加載請求最終都應該傳送到頂層的啟動類加載器中,只有當父類加載器反饋自己無法完成加載請求(它管理的范圍之中沒有這個類)時,子加載器才會嘗試著自己去加載。
使用雙親委托模型來組織類加載器之間的關系,有一個顯而易見的好處就是Java類隨著它的類加載器一起具備了一種帶有優先級的層次關系,例如java.lang.Object存放在rt.jar之中,無論那個類加載器要加載這個類,最終都是委托給啟動類加載器進行加載,因此Object類在程序的各種類加載器環境中都是同一個類,相反,如果沒有雙親委托模型,由各個類加載器去完成的話,如果用戶自己寫一個名為java.lang.Object的類,并放在classpath中,應用程序中可能會出現多個不同的Object類,java類型體系中最基本安全行為也就無法保證。
類加載器SPI:
java.lang.ClassLoader 類提供的幾個關鍵方法:
loadClass: 此方法負責加載指定名字的類,首先會從已加載的類中去尋找,如果沒有找到;從parent ClassLoader[ExtClassLoader]中加載;如果沒有加載到,則從Bootstrap ClassLoader中嘗試加載(findBootstrapClassOrNull方法), 如果還是加載失敗,則拋出異常ClassNotFoundException, 在調用自己的findClass方法進行加載。如果要改變類的加載順序可以覆蓋此方法;如果加載順序相同,則可以通過覆蓋findClass方法來做特殊處理,例如:解密,固定路徑尋找等。當通過整個尋找類的過程仍然未獲取Class對象,則拋出ClassNotFoundException異常。
如果類需要resolve,在調用resolveClass進行鏈接。
protected synchronized Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{// First, check if the class has already been loadedClass c = findLoadedClass(name);if (c == null) {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.c = findClass(name);}}if (resolve) {resolveClass(c);}return c;} findLoadedClass?此方法負責從當前ClassLoader實例對象的緩存中尋找已加載的類,調用的為native方法。 protected final Class<?> findLoadedClass(String name) {if (!checkName(name))return null;return findLoadedClass0(name);}private native final Class findLoadedClass0(String name);findClass 此方法直接拋出ClassNotFoundException異常,因此要通過覆蓋loadClass或此方法來以自定義的方式加載相應的類。
protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);}findSystemClass ?此方法是從sun.misc.Launcher$AppClassLoader中尋找類,如果未找到,則繼續從BootstrapClassLoader中尋找,如果仍然未找到,返回null
protected final Class<?> findSystemClass(String name)throws ClassNotFoundException{ClassLoader system = getSystemClassLoader();if (system == null) {if (!checkName(name))throw new ClassNotFoundException(name);Class cls = findBootstrapClass(name);if (cls == null) {throw new ClassNotFoundException(name);} return cls;}return system.loadClass(name);}defineClass 此方法負責將二進制字節流轉換為Class對象,這個方法對于自定義類加載器而言非常重要。如果二進制的字節碼的格式不符合jvm class文件格式規范,則拋出ClassFormatError異常;如果生成的類名和二進制字節碼不同,則拋出NoClassDefFoundError;如果加載的class是受保護的、采用不同簽名的,或者類名是以java.開頭的,則拋出SecurityException異常。
protected final Class<?> defineClass(String name, byte[] b, int off, int len,ProtectionDomain protectionDomain)throws ClassFormatError{return defineClassCond(name, b, off, len, protectionDomain, true);}// Private method w/ an extra argument for skipping class verificationprivate final Class<?> defineClassCond(String name,byte[] b, int off, int len,ProtectionDomain protectionDomain,boolean verify)throws ClassFormatError{protectionDomain = preDefineClass(name, protectionDomain);Class c = null;String source = defineClassSourceLocation(protectionDomain);try {c = defineClass1(name, b, off, len, protectionDomain, source,verify);} catch (ClassFormatError cfe) {c = defineTransformedClass(name, b, off, len, protectionDomain, cfe,source, verify);}postDefineClass(c, protectionDomain);return c;} resolveClass 此方法負責完成Class對象的鏈接,如果鏈接過,則直接返回。常見異常:
ClassNotFoundException ?這是最常見的異常,產生這個異常的原因為在當前的ClassLoader 中加載類時,未找到類文件,
NoClassDefFoundError ?這個異常是因為 ?加載到的類中引用到的另外類不存在,例如要加載A,而A中盜用了B,B不存在或當前的ClassLoader無法加載B,就會拋出這個異常。
LinkageError 該異常在自定義ClassLoader的情況下更容易出現,主要原因是此類已經在ClassLoader加載過了,重復的加載會造成該異常。
? ? ? ? ? ? ?本文原文鏈接:http://blog.csdn.net/java2000_wl/article/details/8222876 轉載請注明出處!
轉載于:https://www.cnblogs.com/java-wl/archive/2012/11/25/2923111.html
總結
以上是生活随笔為你收集整理的Java虚拟机学习 - 类加载器(ClassLoader)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java程序员从笨鸟到菜鸟之(一百零七)
- 下一篇: OSPF如何选举DR/BDR规则