摘要:以上文中的類的加載過程為例,它的加載器為系統類加載器。自定義加載器編寫自定義加載器并不困難,只要繼承抽象類并覆蓋方法就行了。源碼來自參考資料類加載機制與類加載器架構深入探討類加載器
序
我是在關于Java的面試題里了解到類加載器的,在這之前從未想過Java里類是如何被加載、解析的,一直以為只要Import就好了。事實上Java類加載器是一塊非常重要的內容,可以用在類層次劃分、OSGi、熱部署、代碼加密等領域。即使業務上可能沒有涉及到,了解相關知識對排除BUG也是有幫助的。
類加載器基本概念平時在編寫代碼時,想使用什么類就Import就好了,好像這些類一開始就在JVM里了一樣,現在我們知道這是因為JVM自動為我們加載了這些類。顧名思義,類加載器的工作主要是加載Java字節碼文件(也就是.class文件)到虛擬機里,并解析為java.lang.Class類的一個實例。到這里,被加載的類還是不能像平時一樣直接new一個對象出來的。因為一個類總共要經歷加載、驗證、解析、初始化等4個步驟后才是Java里的一個類型。后面幾個步驟不是本文重點,大家可以自行學習。
類加載器的組成類加載器一共有4種,分別是引導類加載器(bootstrap class loader)、擴展類加載器(extensions class loader)、系統類加載器(system class loader)、自定義加載器,它們之間的加載關系如下圖所示:
其中,除了引導類加載器是用原生代碼實現,其余的加載器都是繼承自抽象類java.lang.ClassLoader。而且系統自帶的3個加載器都有自己的特殊之處。
引導類加載器引導類加載器是用來加載Java的核心庫,像是java.lang包等這些Java應用必備的類都是引導類加載器加載的。加載路徑是<JAVA_HOME>lib目錄中的或者是-Xbootclasspath參數所指定的目錄中,被JVM所識別的文件(通過名字識別,名字必須是rt.jar)。因為引導類加載器是用原生代碼實現的,所以不能在Java代碼中直接引用到引導類加載器。
擴展類加載器顧名思義,擴展類加載器是用來加載Java的擴展類庫。加載路徑是<JAVA_HOME>libext目錄中的或者是java.ext.dirs系統變量所指定的路徑中的所有類庫。
系統類加載器系統類加載器的加載路徑是Java應用的類路徑(CLASSPATH),也就是說在沒有自定義加載器的情況下,Java應用的類都是由系統類加載器加載的。而且該加載器可以用ClassLoader類的getSystemClassLoader()方法直接獲取到。
除了引導類加載器,每個加載器都有一個父加載器。比如加載器A加載了加載器B,那么加載器A就是加載器B的父加載器,可以通過java.lang.ClassLoader的getParent()方法獲取父加載器,而且Java中每個Class對象都維護著一個加載器引用,可以通過getClassLoader()方法獲取加載該類的加載器。
例如下面這段代碼:
public class Main { public static void main(String[] args) { ClassLoader loader = Main.class.getClassLoader(); while (loader != null) { System.out.println(loader.toString()); loader = loader.getParent(); } } }
這里輸出了Main類的加載器與其所有的父加載器,運行結果:
sun.misc.Launcher$AppClassLoader@18b4aac2 sun.misc.Launcher$ExtClassLoader@1540e19d Process finished with exit code 0
我們看到Main類的加載器是系統類加載器,它的父加載器是擴展類加載器。擴展類加載器的父加載器應該是引導類加載器才對,這里沒有輸出是因為有些JDK的實現里在父加載器為引導類加載器的情況下是返回null的。
雙親委托模式第一次看到雙親委托模式這個詞的時候就感覺意義不明,完全不知道是什么意思。在了解了加載器的加載過程之后,才發現是一種代理模式。
以上文中的Main類的加載過程為例,它的加載器為系統類加載器。但是系統類加載器不會直接去加載這個類,而是先委托給它的父加載器,也就是擴展類加載器。同樣,擴展類加載器也會先委托給它的父加載器,一直委托到引導類加載器才開始真正的嘗試加載,如果加載失敗就返回由發出委托的加載器嘗試加載。
這樣做的目的是為了保護Java核心庫和保持類型安全。因為在JVM中判斷兩個類是否相同,不僅僅是看它們的全名是否相同,還要判斷它們的加載器是否相同。通過雙親委托模式就能保證每次加載核心庫的加載器都是引導類加載器,從而防止出現類似于多個java.lang.Object類型這種情況。
自定義加載器編寫自定義加載器并不困難,只要繼承抽象類java.lang.ClassLoader并覆蓋findClass(String name)方法就行了。不建議覆蓋 loadClass(String name)方法,因為這個方法里面封裝了前面提到的雙親委托模式,覆蓋可能會導致該模式失效。
// 源碼來自 https://www.ibm.com/developerworks/cn/java/j-lo-classloader public class FileSystemClassLoader extends ClassLoader { private String rootDir; public FileSystemClassLoader(String rootDir) { this.rootDir = rootDir; } protected Class> findClass(String name) throws ClassNotFoundException { byte[] classData = getClassData(name); if (classData == null) { throw new ClassNotFoundException(); } else { return defineClass(name, classData, 0, classData.length); } } private byte[] getClassData(String className) { String path = classNameToPath(className); try { InputStream ins = new FileInputStream(path); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int bufferSize = 4096; byte[] buffer = new byte[bufferSize]; int bytesNumRead = 0; while ((bytesNumRead = ins.read(buffer)) != -1) { baos.write(buffer, 0, bytesNumRead); } return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } private String classNameToPath(String className) { return rootDir + File.separatorChar + className.replace(".", File.separatorChar) + ".class"; } }參考資料
Java類加載機制與Tomcat類加載器架構
深入探討 Java 類加載器
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76722.html
摘要:如果需要支持類的動態加載或需要對編譯后的字節碼文件進行解密操作等,就需要與類加載器打交道了。雙親委派模型,雙親委派模型,約定類加載器的加載機制。任何之類的字節碼都無法調用方法,因為該方法只能在類加載的過程中由調用。 jvm系列 垃圾回收基礎 JVM的編譯策略 GC的三大基礎算法 GC的三大高級算法 GC策略的評價指標 JVM信息查看 GC通用日志解讀 jvm的card table數據...
摘要:最終形成可以被虛擬機最直接使用的類型的過程就是虛擬機的類加載機制。即重寫一個類加載器的方法驗證驗證是連接階段的第一步,這一階段的目的是為了確保文件的字節流中包含的信息符合當前虛擬機的要求,并且不會危害虛擬機自身的安全。 《深入理解Java虛擬機:JVM高級特性與最佳實踐(第二版》讀書筆記與常見相關面試題總結 本節常見面試題(推薦帶著問題閱讀,問題答案在文中都有提到): 簡單說說類加載過...
摘要:看到的只是,而由泛型附加的類型信息對來說是不可見的。然后再加載執行類的靜態變量以及靜態語句塊。接口中基本數據類型為而抽類象不是的。本地方法接口主要是調用或實現的本地方法及返回結果。用戶自定義類加載器,在程序運行期間,通過的子類動態加載。 編譯機制 編譯主要是把?.Java文件轉換為 .class 文件。其中轉換后的 .class 文件就包含了元數據,方法信息等一些信息。比如說元數據就...
摘要:如問到是否使用某框架,實際是是問該框架的使用場景,有什么特點,和同類可框架對比一系列的問題。這兩個方向的區分點在于工作方向的側重點不同。 [TOC] 這是一份來自嗶哩嗶哩的Java面試Java面試 32個核心必考點完全解析(完) 課程預習 1.1 課程內容分為三個模塊 基礎模塊: 技術崗位與面試 計算機基礎 JVM原理 多線程 設計模式 數據結構與算法 應用模塊: 常用工具集 ...
閱讀 1835·2021-11-25 09:43
閱讀 1344·2021-11-22 15:08
閱讀 3746·2021-11-22 09:34
閱讀 3228·2021-09-04 16:40
閱讀 3027·2021-09-04 16:40
閱讀 546·2019-08-30 15:54
閱讀 1338·2019-08-29 17:19
閱讀 1755·2019-08-28 18:13