JVM系列筆記目錄
- 虛擬機的基礎概念
- class檔案結構
- class檔案加載程序
- jvm記憶體模型
- JVM常用指令
- GC與調優
Class檔案加載程序
JVM加載Class檔案主要分3個程序:Loading 、Linking、Initialzing
1.Loading
Loading的程序就是通過類加載器將.class檔案加載到jvm記憶體中程序,需要理解雙親委派機制、類加載器ClassLoader,加載程序如下,

ClassLoader
不同的類加載器加載范圍不一樣,以Java8中的為例,
- BootClassLoader 加載范圍
sun.boot.class.paht- ExtClassLoader 加載范圍
java.ext.dirs- AppClassLoader 加載范圍
java.class.path- CustomClassLoader 可自定義加載范圍
前三個加載器來自JDK的Launcher類,三個ClassLoader作為Launcher的內部類,感興趣可以查看下原始碼,

開發者也可以自定義的ClassLoader,自定義記載范圍,
雙親委派機制
自底向上檢查該類是否已經加載,parent方向;自頂向下進行類的實際查找和加載,child方向,
類的加載遵循雙親委派機制,主要是出于安全的考慮,雙親委派機制是如何實作的,下面原始碼會解釋,

注意:雙親委派中存在所謂的父加載器并不是加載器的加載器,只是翻譯的問題,別混淆了類的繼承概念,
ClassLoader原始碼

ClassLoader原始碼中比較重要的一個函式是loadClass(),執行程序是:findLoadedClass()->parrent.loadClass()->findClass(),第一步是自底向上查詢是否已經加載,第二步是自頂向下查找加載類,這里就規定或是說實作了雙親委派機制,詳細見ClassLoader的原始碼,
自定義ClassLoader
如何自定義ClassLoader?可以繼承ClassLoader類,重新自己的findClass(),在里面呼叫defineClass()來實作自定義加載特定范圍的類,
如何打破雙親委派機制,哪種情形下打破過?
從上面的ClassLoader原始碼中大概能看出是如何實作了雙親委派機制的,從這入手可以通過2種方式打破該機制:
- super(parent)指定parent會打破該機制
- 自定義ClassLoader重寫
loadClass()也可以打破
何時打破過?雙親委派機制并不是不能打破,某些特殊場景下也會選擇打破該機制,
- JDK 1.2之前,自定義ClassLoader必須重寫
loadClass(),打破過,- 執行緒ThreadContextClassLoader可以實作基礎類呼叫實作類代碼,通過thread.setContextClassLoader指定,
- 熱啟動熱部署,如tomcat都有自己模塊指定的classloader,可以加載同一類別庫的不同版本,
Class執行方式
Class執行方式分為3種:解釋執行、編譯執行、混合執行,各有優缺點,可通過引數指定,
- 1.解釋執行:使用bytecode intepreter 解釋器解釋執行,該模式啟動很快,執行稍慢,可通過
-Xint引數指定該模式, - 2.編譯執行:使用 Just in time Complier JIT編譯器編譯執行,該模式執行很快,編譯很慢,可通過
-Xcomp引數指定該模式, - 3.混合執行:默認的模式,解釋器+熱點代碼編譯,開始解釋執行,啟動較快,對熱點代碼進行實時監測和編譯成本地代碼執行,可通過
-Xmixed引數指定該模式,
熱點代碼監測:多次被呼叫的方法用方法計數器,多次被呼叫的回圈用回圈計數器,可通過引數
-XX:CompileThreshold = 10000指定觸發JIT編譯的閾值,
2.Linking
Linking鏈接的程序分3個階段:Vertification、Preparation、Resolution,
- Vertification: 驗證Class檔案是否符合JVM規定,
- Preparation:給靜態成員變數賦默認值
- Resolution:將類、方法、屬性等符號參考解釋為直接參考;常量池中的各種符號參考解釋為指標、偏移量等記憶體地址的直接參考
3. Initializing
呼叫初始化代碼clint,給靜態成員變數賦初始值,
這里可以了解下必須初始化的5種情況:
new getstatic putstatic invokestatic指令,訪問final變數除外java.lang.reflect對類進行反射呼叫時- 初始化子類的時候,父類必須初始化
- 虛擬機啟動時,被執行的主類必須初始化
- 動態語言支持
java.lang.invoke.MethodHandler解釋的結果為REF_getstatic REF_putstatic REF_invokestatic的方法句柄時,該類必須初始化,
4.總結思考
設計模式中單例模式的雙重檢查的實作,INSTANCE是否需要加valatile?
public class Mgr06 {
// 是否需要加volatile?
private static volatile Mgr06 INSTANCE;
private Mgr06() {
}
public static Mgr06 getInstance() {
if (INSTANCE == null) {
//雙重檢查
synchronized (Mgr06.class) {
if(INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// new 了物件,不為null,但未完成變數的初始化復制,物件處于半初始化狀 態,其它執行緒有可能取到半初始化的物件,
INSTANCE = new Mgr06();
}
}
}
return INSTANCE;
}
}
個人認為是需要加的,思考方向, class檔案load到記憶體,給靜態變數賦默認值,再賦初始值,new 物件的時候,首先要申請記憶體空間,然后給成員變數賦默認值,接下來給成員變數賦初始值,這個程序中物件有可能處于半初始化狀態,多執行緒并發下別的執行緒有可能取到半初始化的物件,加volatile可保證執行緒的可見性,
知識分享,轉載請注明出處,學無先后,達者為先!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/166751.html
標籤:Java
上一篇:JAVA的多執行緒與高并發(二)、Synchronized
下一篇:mybatis_6日志工廠
