jvm賦能java跨平臺的能力,而類載入機制是深入理解java的必要條件。
java的類載入機制是怎樣的?什麼是雙親委派原則?
答:java的類載入過程分為 載入,連結,初始化。載入:即從資料來源(jar,class,網路)載入class檔案到jvm,對映為class物件,如果不是classFile結構,丟擲ClassFormatError;連結:把第一步得到的class物件轉換到jvm環境,進行驗證(位元組資訊是否符合jvm規範,否則丟擲VerifyError),準備(為靜態變數分配記憶體空間),解析(常量池中的符號引用替換為直接引用);初始化:即為靜態變數和靜態程式碼塊賦值。
java的類載入器部分的載入器分4類,見上圖。
BootStrapClassLoader:載入jre/lib下的jdk的jar包,具有超級許可權,是最頂級的類載入器;
ExtensionClassLoader:載入jre/lib/ext下的jar包,載入jdk的擴充套件程式包;
ApplicationClassLoader:載入當前應用classpath下的jar包或者class檔案;
自定義ClassLoader:使用者自定義的類載入器,一般是為了進行程序隔離,或者自己操縱位元組碼;
雙親委派機制:即為了避免類資訊被重複載入和程式的安全性,父載入器優先子載入器載入型別到jvm,子載入器無法載入父載入器已經載入到jvm中的型別資訊。
下面做一下擴充套件:針對面試官可能會追問的細節。
指定超級載入器的目錄和時機:
指定新的bootclasspath,替換java.*包的內部實現
java -Xbootclasspath: your_App
a意味著append,將指定目錄新增到bootclasspath後面
java -Xbootclasspath/a: your_App
p意味著prepend,將指定目錄新增到bootclasspath前面
java -Xbootclasspath/p: your_App
指定擴充套件載入器的目錄:
java -Djava.ext.dirs=your_ext_dir HelloWorld
指定系統載入器的實現類:
java -Djava.system.class.loader=com.yourcorp.YourClassLoader HelloWorld
打印出類載入器:
java package org.example.mianshi.classloader;import java.util.Collection;/**作者: carter建立日期: 2020/3/31 12:41描述: 類載入器的層級關係/public class PrintClassLoaderApp {public static void main(String[] args) { System.out.println("PrintClassLoaderApp 的類載入器是:"+PrintClassLoaderApp.class.getClassLoader()); System.out.println("parent 的類載入器是:"+ PrintClassLoaderApp.class.getClassLoader().getParent()); System.out.println("parent.parent 的類載入器是:"+ PrintClassLoaderApp.class.getClassLoader().getParent().getParent()); System.out.println("Collection 的類載入器是:"+ Collection.class.getClassLoader());}}
輸出結果:
我使用的java10,PlatformClassLoader替代了ExtensionClassLoader;
PrintClassLoaderApp 的類載入器是:jdk.internal.loader.ClassLoaders$AppClassLoader@4459eb14parent 的類載入器是:jdk.internal.loader.ClassLoaders$PlatformClassLoader@2ac1fdc4parent.parent 的類載入器是:nullCollection 的類載入器是:null
自定義類載入器
自定義類載入器一般用來進行程序內隔離,或者需要自己操縱位元組碼的場景。
過程如下:1,通過名稱找到二進位制程式碼,即class檔案;2,使用class檔案建立對應的class物件;
示例程式碼如下:
package org.example.mianshi.classloader;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.IOException;import java.io.InputStream;/** * 作者: carter * 建立日期: 2020/3/31 14:24 * 描述: 自定義類載入器 */public class CustomerClassLoaderApp extends ClassLoader { @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] bytes = loadClassFromFile(name); return defineClass(name, bytes, 0, bytes.length); } private byte[] loadClassFromFile(String name) { InputStream inputStream = getClass().getClassLoader() .getResourceAsStream(name.replace(".", File.separator) + ".class"); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); int nextValue = 0; try { while ((nextValue = inputStream.read()) != -1) { byteArrayOutputStream.write(nextValue); } } catch (IOException e) { e.printStackTrace(); } byte[] buffer = byteArrayOutputStream.toByteArray(); return buffer; }}
如何加速類載入1, AOT , 即提前把位元組碼編譯成機器碼,然後在啟動的時候指定機器碼的位置,2,AppCDS ,即提前把類資訊載入成為元資料,使用記憶體對映技術,免除類載入和解析的開銷。
小結本節回顧了jvm的類載入過程,類載入器的層次,雙親委派原則,然後指明了自定義類載入器的使用場景和基本過程,以及給了一個簡單的例子;最後給出了兩種加速類載入器速度的方法。