1. class裝載驗(yàn)證流程
1.1. 加載 裝載類的第一個(gè)階段, 取得類的二進(jìn)制流,轉(zhuǎn)為方法區(qū)數(shù)據(jù)結(jié)構(gòu),在Java堆中生成對(duì)應(yīng)的java.lang.Class對(duì)象
1.2. 鏈接
-
1.2.1 驗(yàn)證
保證Class流的格式是正確的- 文件格式驗(yàn)證
- 是否以0xCAFEBABE開(kāi)頭
- 版本號(hào)是否合理
- 1.2.2 元數(shù)據(jù)驗(yàn)證
- 是否有父類
- 繼承了final類?
- 非抽象類實(shí)現(xiàn)了所有的抽象方法
- 1.2.3 字節(jié)碼驗(yàn)證 (很復(fù)雜)
通過(guò)驗(yàn)證的class也不一定是沒(méi)有問(wèn)題的- 運(yùn)行檢查
- 棧數(shù)據(jù)類型和操作碼數(shù)據(jù)參數(shù)吻合
- 跳轉(zhuǎn)指令指定到合理的位置
- 1.2.4 符號(hào)引用驗(yàn)證
- 常量池中描述類是否存在
- 訪問(wèn)的方法或字段是否存在且有足夠的權(quán)限
- 文件格式驗(yàn)證
-
準(zhǔn)備
- 分配內(nèi)存,并為類設(shè)置初始值 (方法區(qū)中)
- public static int v=1;
- 在準(zhǔn)備階段中,v會(huì)被設(shè)置為0
- 在初始化的<clinit>中才會(huì)被設(shè)置為1
- 對(duì)于static final類型,在準(zhǔn)備階段就會(huì)被賦上正確的值(常量在準(zhǔn)備時(shí)就會(huì)被賦上正確的值)
public static final int v=1;
- 分配內(nèi)存,并為類設(shè)置初始值 (方法區(qū)中)
-
解析
- 符號(hào)引用
字符串引用對(duì)象不一定被加載替換為直接引用指針或者地址偏移量引用對(duì)象一定在內(nèi)存
- 符號(hào)引用
1.3. 初始化
- 執(zhí)行類構(gòu)造器<clinit>
- static變量 賦值語(yǔ)句
- static{}語(yǔ)句
- 子類的<clinit>調(diào)用前保證父類的<clinit>被調(diào)用
- <clinit>是線程安全的
- 思考:Java.lang.NoSuchFieldError錯(cuò)誤可能在什么階段拋出
什么是類裝載器
- ClassLoader是一個(gè)抽象類
- ClassLoader的實(shí)例將讀入Java字節(jié)碼將類裝載到JVM中
- ClassLoader可以定制,滿足不同的字節(jié)碼流獲取方式
- ClassLoader負(fù)責(zé)類裝載過(guò)程中的加載階段
JDK中ClassLoader默認(rèn)設(shè)計(jì)模式
- 重要方法
public Class<?> loadClass(String name) throws ClassNotFoundException
載入并返回一個(gè)Class
protected final Class<?> defineClass(byte[] b, int off, int len)
定義一個(gè)類,不公開(kāi)調(diào)用
protected Class<?> findClass(String name) throws ClassNotFoundException
loadClass回調(diào)該方法,自定義ClassLoader的推薦做法
protected final Class<?> findLoadedClass(String name)
尋找已經(jīng)加載的類(找不到才要去加載類)
- 分類
BootStrap ClassLoader啟動(dòng)類加載器
Extension ClassLoader擴(kuò)展類加載器
App ClassLoader應(yīng)用/系統(tǒng)類加載器
Custom ClassLoader自定義類加載器
啟動(dòng)類加載器沒(méi)有parent,因?yàn)樗亲铐攲?,其他每個(gè)ClassLoader都有一個(gè)Parent作為父親
參考
自底向上:檢查類是否已被加載
自頂向下:嘗試加載類

類加載器關(guān)系.png
-Xbootclasspath 可以手動(dòng)指定 boot classpath
類加載例子:
位于:D:\helloJVM\HelloClassLoader.java:
public class HelloClassLoader {
public void print(){
System.out.println("I am in bootloader");
}
}
應(yīng)用中的HelloClassLoader.java:
public class HelloClassLoader {
public void print(){
System.out.println("I am in apploader");
}
}
及定位class是被哪個(gè)loader定位的應(yīng)用程序:
public class LookForClassLoader {
public static void main(String[] args) {
HelloClassLoader loader=new HelloClassLoader();
loader.print();
}
-
直接執(zhí)行main方法(即用app loader來(lái)加載 HelloClassLoader )
loader.print()結(jié)果.png 添加參數(shù)
-Xbootclasspath/a:D:\helloJVM,并編譯HelloClassLoader.java。執(zhí)行main方法
D:\>cd helloJVM
D:\helloJVM>javac HelloClassLoader.java

要記得編譯哦.png

loader.print()結(jié)果
- I am in apploader 在classpath中卻沒(méi)有被加載,說(shuō)明類的加載是從上往下
- 保持2的條件不變,強(qiáng)制在app 類加載器中加載classPath中的 HelloClassLoader
public class LookForClassLoader {
public static void main(String[] args) {
try {
forceLoadInAppLoader();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void forceLoadInAppLoader() throws Exception {
// 強(qiáng)制在apploader中加載 HelloClassLoader
ClassLoader cl = LookForClassLoader.class.getClassLoader();
byte[] bHelloClassLoader = loadClassBytes("jvmstudy.HelloClassLoader");
Method md_defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
md_defineClass.setAccessible(true);
md_defineClass.invoke(cl, bHelloClassLoader, 0, bHelloClassLoader.length);
md_defineClass.setAccessible(false);
HelloClassLoader loader = new HelloClassLoader();
System.out.println(loader.getClass().getClassLoader());
loader.print();
}
private static byte[] loadClassBytes(String className) throws IOException {
//獲取class文件路徑
String classFile = getClassFile(className);
FileInputStream fis = null;
try {
fis = new FileInputStream(classFile);
} catch (FileNotFoundException e) {
System.out.println(e);
return null;
}
byte[] bytes = new byte[fis.available()];
fis.read(bytes);
fis.close();
return bytes;
}
private static String getClassFile(String name) {
StringBuffer sb = new StringBuffer("E:\\myspace\\local-git-hub\\Steping\\src\\");
name = name.replace('.', File.separatorChar) + ".class";
sb.append(File.separatorChar + name);
return sb.toString();
}
}

loader.print()結(jié)果.png
- 在查找類的時(shí)候,先在底層的Loader查找,是從下往上的。app Loader能找到,就不會(huì)去上層加載器加載
即雙親委派模式,但是其問(wèn)題也就是在于頂層classLoader 無(wú)法加載底層classLoader中的類
- Java框架(rt.jar)如何加載應(yīng)用的類?
在app Loader中包含一個(gè)rt接口的實(shí)例
javax.xml.parsers包中定義了xml解析的類接口,Service Provider Interface SPI 位于rt.jar 。即接口在啟動(dòng)ClassLoader中,而SPI的實(shí)現(xiàn)類,在AppLoader。
Thread. setContextClassLoader() 即上下文加載器
是一個(gè)角色/職責(zé),任何加載器都可以來(lái)承擔(dān)這個(gè)角色,就像班長(zhǎng)。
用以解決頂層ClassLoader無(wú)法訪問(wèn)底層ClassLoader的類的問(wèn)題
基本思想是,在頂層ClassLoader中,傳入底層ClassLoader的實(shí)例
rt.jar 中javax.xml.parsers.FactoryFinder展示如何在啟動(dòng)類加載器加載AppLoader的類 ,即上下文ClassLoader可以突破雙親模式的局限性
/**
* Attempt to load a class using the class loader supplied. If that fails
* and fall back is enabled, the current (i.e. bootstrap) class loader is
* tried.
*
* If the class loader supplied is <code>null</code>, first try using the
* context class loader followed by the current (i.e. bootstrap) class
* loader.
*
* Use bootstrap classLoader if cl = null and useBSClsLoader is true
*/
static private Class<?> getProviderClass(String className, ClassLoader cl,
boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException
{
try {
if (cl == null) {
if (useBSClsLoader) {
return Class.forName(className, false, FactoryFinder.class.getClassLoader());
} else {
cl = ss.getContextClassLoader();
if (cl == null) {
throw new ClassNotFoundException();
}
else {
return Class.forName(className, false, cl);
}
}
}
else {
return Class.forName(className, false, cl);
}
}
catch (ClassNotFoundException e1) {
if (doFallback) {
// Use current class loader - should always be bootstrap CL
return Class.forName(className, false, FactoryFinder.class.getClassLoader());
}
else {
throw e1;
}
}
}
打破常規(guī)模式
- 雙親模式是默認(rèn)的模式,但不是必須這么做
- Tomcat的WebappClassLoader 就會(huì)先加載自己的Class,找不到再委托parent
- OSGi的ClassLoader形成網(wǎng)狀結(jié)構(gòu),根據(jù)需要自由加載Class
破壞雙親模式例子:
OrderClassLoaders
熱替換`當(dāng)一個(gè)class被替換后,系統(tǒng)無(wú)需重啟,替換的類立即生效
`
很不錯(cuò)的Java類加載器(ClassLoader) 推薦
