jvm學習筆記6:類載入器
目錄
1. 類與類載入器
對於任意一個類,都需要有載入它的類載入器和這個類本身一同確認其在java虛擬機器中的唯一性,每一個類載入器,都擁有一個獨立的類名稱空間。
2. 雙親委派機制
2.1 載入器分類
從java虛擬機器的角度來看,只存在兩種不同的類載入器:
- 啟動類載入器(Bootstrap ClassLoader):C++語言實現,是java虛擬機器的一部分;
- 其他所有的類載入器:這些都是有Java語言實現的,獨立於虛擬機器外部,並且都繼承自抽象類java.lang.ClassLoader。
從開發人員的角度來看,絕大部分Java程式都會使用以下三種類載入器,可以分為:
- 啟動類載入器(
Bootstrap ClassLoader
):負責<JAVA_HOME>/lib或者被-Xbootclasspath引數指定的路徑。 - 擴充套件類載入器(Extension ClassLoader):這個載入器由sun.misc.Launcher$ExtClassLoader實現。負責載入<JAVA_HOME>/lib/ext目錄以及被java.ext.dirs系統引數指定的類庫。
- 應用類載入器(Application ClassLoader):這個類由sun.misc.Launcher$AppClassLoader實現。載入使用者類路徑(classPath)的類。
2.2 從原始碼來看雙親委派機制
雙親委派機制的圖示如下,其其工作流程是:如果一個類載入器收到了類載入的請求,它首先不會自己去嘗試載入這個類,而是把請求委派其父類載入器去完成,每一個層次的類載入器都是如此,因此最終所有的載入請求都應該傳遞給頂層的啟動類載入器,只有父類載入器無法完成這個載入請求時,子載入器才會嘗試自己去載入。
除了用C++實現的啟動類載入器,其餘所有的類載入器都要繼承抽象類ClassLoader方法,下面分析其中幾個關鍵方法的原始碼:
2.3.1 構造方法
//指定一個父類載入器,指定null,則父類載入器為Bootstrap ClassLoader
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}
//預設父類載入器為AppClassLoader
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
2.3.2 loadClass(String name, boolean resolve)
/**
* 雙親委派機制是通過loadClass方法實現的
*/
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先校驗類是不是已經載入了
Class<?> c = findLoadedClass(name);
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) {
//如果父類載入器都沒有載入成功,則呼叫自己的findClass 方法進行載入
long t1 = System.nanoTime();
c = findClass(name);
// 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;
}
}
2.3.3 findClass(String name)
ClassLoder
中的findClass
方法是抽象方法,需要子類進行實現。ExtClassLoader
和AppClassLoader
都繼承了URLClassLoader
,而URLClassLoader
繼承了ClassLoder
,並實現了findClass
方法。
protected Class<?> findClass(final String name)
throws ClassNotFoundException
{
final Class<?> result;
try {
result = AccessController.doPrivileged(
new PrivilegedExceptionAction<Class<?>>() {
public Class<?> run() throws ClassNotFoundException {
String path = name.replace('.', '/').concat(".class");
//找到對應的clss資源
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
//根據class位元組流生產Class例項
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
return null;
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}
2.3.4 defineClass
所有的類載入器最後都要掉這個方法,它內部會調本地方法,負責將class位元組流檔案轉換成Class例項。
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
//先進行校驗,比如以java開頭的類名,不能載入。
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;
}
3. 自定義類載入器
- 如果要打破雙親委派機制,重寫
loadClass()
方法; - 否則,重寫
findClass()
方法即可。
3.1 自定義類載入器(載入指定目錄)
package com.lihui.study.jvm;
import java.io.*;
/**
* @author ex_lihui4
* @ClassName MyClassLoader
* @Description 自定義一個普通的類載入器,通過dir執行相應的類目錄
* @date 2020-11-17 11:50
*/
public class MyClassLoader extends ClassLoader {
private String dir;
public MyClassLoader(ClassLoader parent, String dir) {
super(parent);
this.dir = dir;
}
public MyClassLoader(String dir) {
//如不指定父類載入器,
this.dir = dir;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
//拼接類路徑
String className = name.substring(name.lastIndexOf(".") + 1)+".class";
String classPth = "\\"+name.substring(0, name.lastIndexOf(".")).replace(".","\\")+"\\";
String fileName = dir+classPth+className;
//獲取二進位制資料
File file = new File(fileName);
try (FileInputStream is = new FileInputStream(file)){
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length = 0;
while ((length = is.read(buffer)) !=-1){
os.write(buffer,0,length);
}
byte[] bytes = os.toByteArray();
//返回類檔案
return defineClass(name,bytes,0,bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
相關文章
- JVM學習筆記——類載入機制JVM筆記
- 《深入理解java虛擬機器》學習筆記6——類載入機制Java虛擬機筆記
- JVM學習-虛擬機器類載入機制JVM虛擬機
- 類載入器(JVM)JVM
- 【JVM】JVM之類載入器JVM
- JVM學習(三)——類載入機制JVM
- JVM入門--類載入器JVM
- JVM筆記 -- 來,教你類載入子系統JVM筆記
- JVM學習筆記JVM筆記
- Java-JVM-類載入器JavaJVM
- 虛擬機器類載入機制(深入Jvm讀書筆記二)虛擬機JVM筆記
- JVM學習筆記——Class類檔案解讀JVM筆記
- JVM學習筆記——初識JVMJVM筆記
- XV6學習筆記(1) : 啟動與載入筆記
- 【隨筆】JVM核心:JVM執行和類載入JVM
- JVM學習筆記(4)---垃圾收集器JVM筆記
- JVM學習筆記---伺服器,JVM效能監控工具JVM筆記伺服器
- JVM 學習筆記(五)JVM筆記
- JVM學習筆記-01JVM筆記
- 今日學習JVM筆記JVM筆記
- JVM核心學習筆記JVM筆記
- 每天學習一點 JVM 之:類載入機制JVM
- JVM之類載入器ClassLoaderJVM
- JVM類載入器-原始碼分析JVM原始碼
- 深入理解JVM類載入器JVM
- AS 學習筆記 載入資料筆記
- jvm類載入JVM
- [JVM]類載入JVM
- JVM學習筆記五--虛擬機器棧JVM筆記虛擬機
- 深入理解JVM讀書筆記三: 虛擬機器類載入機制JVM筆記虛擬機
- 深入理解JVM虛擬機器6:深入理解JVM類載入機制JVM虛擬機
- JVM虛擬機器和類載入器JVM虛擬機
- Java的類載入器初學習Java
- 【JVM進階之路】十四:類載入器和類載入機制JVM
- PHP 手冊 (類與物件) 學習筆記四:類的自動載入PHP物件筆記
- JVM系列之類載入流程-自定義類載入器JVM
- JVM之類載入器子系統JVM
- 【JVM】類載入器與雙親委派JVM