摘要:類加載過程共分為加載驗證準備解析初始化使用和卸載七個階段這些階段通常都是互相交叉的混合式進行的,通常會在一個階段執行的過程中調用或激活另外一個階段。
JVM類加載過程共分為加載、驗證、準備、解析、初始化、使用和卸載七個階段
這些階段通常都是互相交叉的混合式進行的,通常會在一個階段執行的過程中調用或激活另外一個階段。
加載過程是JVM類加載的第一步,如果JVM配置中打開-XX:+TraceClassLoading,我們可以在控制臺觀察到類似
[Loaded chapter7.SubClass from file:/E:/EclipseData-Mine/Jvm/build/classes/]
的輸出,這就是類加載過程的日志。
加載過程是作為程序猿最可控的一個階段,因為你可以隨意指定類加載器,甚至可以重寫loadClass方法,當然,在jdk1.2及以后的版本中,loadClass方法是包含雙親委派模型的邏輯代碼的,所以不建議重寫這個方法,而是鼓勵重寫findClass方法。
類加載的二進制字節碼文件可以來自jar包、網絡、數據庫以及各種語言的編譯器編譯而來的.class文件等各種來源。
加載過程主要完成如下三件工作:
1>通過類的全限定名(包名+類名)來獲取定義此類的二進制字節流
2>將字節流所代表的靜態存儲結構轉化為運行時數據結構存儲在方法區
3>為類生成java.lang.Class對象,并作為該類的唯一入口
這里涉及到一個概念就是類的唯一性,書上對該概念的解釋是:在類的加載過程中,一個類由類加載器和類本身唯一確定。也就是說,如果一個JVM虛擬機中有多個不同加載器,即使他們加載同一個類文件,那得到的java.lang.Class對象也是不同的。因此,只有在同一個加載器中,一個類才能被唯一標識,這叫做類加載器隔離。
驗證驗證過程相對來說就有復雜一點了,不過驗證過程對JVM的安全還是至關重要的,畢竟你不知道比人的代碼究竟能干出些什么。
驗證過程主要包含四個驗證過程:
1>文件格式驗證
四個驗證過程中,只有格式驗證是建立在二進制字節流的基礎上的。格式驗證就是對文件是否是0xCAFEBABE開頭、class文件版本等信息進行驗證,確保其符合JVM虛擬機規范。
2>元數據驗證
元數據驗證是對源碼語義分析的過程,驗證的是子類繼承的父類是否是final類;如果這個類的父類是抽象類,是否實現了起父類或接口中要求實現的所有方法;子父類中的字段、方法是否產生沖突等,這個過程把類、字段和方法看做組成類的一個個元數據,然后根據JVM規范,對這些元數據之間的關系進行驗證。所以,元數據驗證階段并未深入到方法體內。
3>字節碼驗證
既然元數據驗證并未深入到方法體內部,那么到了字節碼驗證過程,這一步就不可避免了。字節碼主要是對方法體內部的代碼的前后邏輯、關系的校驗,例如:字節碼是否執行到了方法體以外、類型轉換是否合理等。
當然,這很復雜。
所以,即使是到了如今jdk1.8,也還是無法完全保證字節碼驗證準確無遺漏的。而且,如果在字節碼驗證浪費了大量的資源,似乎也有些得不償失。
4>符號引用驗證
符號引用的驗證其實是發生在符號引用向直接引用轉化的過程中,而這一過程發生在解析階段。
因為都是驗證,所以一并在這講。符號引用驗證做的工作主要是驗證字段、類方法以及接口方法的訪問權限、根據類的全限定名是否能定位到該類等。具體過程會在接下來的解析階段進行分析。
好了,驗證階段的工作基本就是以上四類,下面我們來看下一個階段。
相信經歷過艱辛的驗證階段的磨練,JVM和我們都倍感疲憊。所以,接下來的準備階段給我們提供了一個相對輕松的休息階段。
準備階段要做的工作很簡單,他瞄準了類變量這個元數據,把他放進了方法區并進行了初始化,這里的初始化并不是
這一部分我畫了幾個圖,內容有些多,放在另一篇文章里:解析
初始化初始化階段是我們可以大搞實驗的一塊實驗田。首先,初始化階段做什么?這個階段就是執行
那么問題來了,啥時候才會觸發一個類的初始化的操作呢?答案有且只有五個:
1>在類沒有進行過初始化的前提下,當執行new、getStatic、setStatic、invokeStatic字節碼指令時,類會立即初始化。對應的java操作就是new一個對象、讀取/寫入一個類變量(非final類型)或者執行靜態方法。
2>在類沒有進行過初始化的前提下,當一個類的子類被初始化之前,該父類會立即初始化。
3>在類沒有進行過初始化的前提下,當包含main方法時,該類會第一個初始化。
4>在類沒有進行過初始化的前提下,當使用java.lang.reflect包的方法對類進行反射調用時,該類會立即初始化。
5>在類沒有進行過初始化的前提下,當使用JDK1.5支持時,如果一個java.langl.incoke.MethodHandle實例最后的解析結果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且這個方法句柄所對應的類沒有進行過初始化,則需要先觸發其初始化。
以上五種情況被稱作類的五種主動引用,除此之外的任何情況都被相應地叫做被動引用。以下是集中常見的且容易迷惑人心智的被動引用的示例:
/** 通過子類引用父類的類變量不會觸發子類的初始化操作 */ public class SuperClass { public static String value = "superClass value"; static { System.out.println("SuperClass init!"); } } public class SubClass extends SuperClass implements SuperInter{ static { System.out.println("SubClass init!"); } } public class InitTest { static { System.out.println("InitTest init!");//main第一個初始化 } public static void main(String[] args) { System.out.println(SubClass.value); } } /** output: InitTest init! SuperClass init! superClass value */
/** 通過定義對象數組的方式是不能觸發對象初始化的 */ public static void main(String[] args) { SubClass[] superArr = new SubClass[10]; } /** output: InitTest init! */
/** 引用類的final類型的類變量無法觸發類的初始化操作 */ public class SuperClass { public static final String CONSTANT_STRING = "constant"; static { System.out.println("SuperClass init!"); } } public class InitTest { static { System.out.println("InitTest init!");//main } public static void main(String[] args) { System.out.println(SuperClass.CONSTANT_STRING);//getStatic } } /** output: InitTest init! constant */
了解了什么時候出發初始化操作后,那么初始化操作的執行順序是什么樣的?并發初始化情況下的運行機制又如何?
JVM虛擬機規定了幾條標準:
先父類后子類,(源碼中)先出現先執行
向前引用:一個類變量在定義前可以賦值,但是不能訪問。
非必須:如果一個類或接口沒有類變量的賦值動作和static代碼塊,那就不生成
執行接口的
同步性:
我們通過一個實例來驗證線程的阻塞問題:
public class SuperClass { static { System.out.println("SuperClass init!"); System.out.println("Thread.currentThread(): " + Thread.currentThread() + " excuting..."); try { Thread.sleep(1000 * 5); } catch (InterruptedException e) { e.printStackTrace(); } } } public class InitTest { static { System.out.println("InitTest init!");//main } public static void main(String[] args) throws ClassNotFoundException, InterruptedException { currentInitTest(); } public static void currentInitTest() throws InterruptedException { Runnable run = new Runnable() { @Override public void run() { System.out.println("Thread.currentThread(): " + Thread.currentThread() + " start"); new SuperClass(); System.out.println("Thread.currentThread(): " + Thread.currentThread() + " end"); } }; Thread[] threadArr = new Thread[10]; for (int i = 0; i < 10; i++) { threadArr[i] = new Thread(run); } for (Thread thread : threadArr) { thread.start(); } } } /** output: InitTest init! Thread.currentThread(): Thread[Thread-0,5,main] start Thread.currentThread(): Thread[Thread-1,5,main] start Thread.currentThread(): Thread[Thread-2,5,main] start Thread.currentThread(): Thread[Thread-7,5,main] start Thread.currentThread(): Thread[Thread-6,5,main] start Thread.currentThread(): Thread[Thread-3,5,main] start Thread.currentThread(): Thread[Thread-5,5,main] start Thread.currentThread(): Thread[Thread-9,5,main] start Thread.currentThread(): Thread[Thread-4,5,main] start Thread.currentThread(): Thread[Thread-8,5,main] start SuperClass init! Thread.currentThread(): Thread[Thread-0,5,main] excuting... Thread.currentThread(): Thread[Thread-9,5,main] end Thread.currentThread(): Thread[Thread-3,5,main] end Thread.currentThread(): Thread[Thread-6,5,main] end Thread.currentThread(): Thread[Thread-7,5,main] end Thread.currentThread(): Thread[Thread-0,5,main] end Thread.currentThread(): Thread[Thread-5,5,main] end Thread.currentThread(): Thread[Thread-4,5,main] end Thread.currentThread(): Thread[Thread-8,5,main] end Thread.currentThread(): Thread[Thread-1,5,main] end Thread.currentThread(): Thread[Thread-2,5,main] end */
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70881.html
摘要:用一張思維導圖盡可能囊括一下的類加載過程的全流程。本文參考自來自周志明深入理解虛擬機第版,拓展內容建議讀者可以閱讀下這本書。 用一張思維導圖盡可能囊括一下JVM的類加載過程的全流程。 本文參考自來自周志明《深入理解Java虛擬機(第2版)》,拓展內容建議讀者可以閱讀下這本書。 showImg(http://ocxhn1mzz.bkt.clouddn.com/class%20loadin...
摘要:今日最佳對于程序員而言,所謂的二八定律指的是花百分之八十的時間去學習日常研發中不常見的那百分之二十的原理。 【今日最佳】對于程序員而言,所謂的二八定律指的是 花百分之八十的時間去學習日常研發中不常見的那百分之二十的原理。 據說阿里某程序員對書法十分感興趣,退休后決定在這方面有所建樹。于是花重金購買了上等的文房四寶。 一日,飯后突生雅興,一番磨墨擬紙,并點上了上好的檀香,頗有王羲之風范,...
摘要:如果需要支持類的動態加載或需要對編譯后的字節碼文件進行解密操作等,就需要與類加載器打交道了。雙親委派模型,雙親委派模型,約定類加載器的加載機制。任何之類的字節碼都無法調用方法,因為該方法只能在類加載的過程中由調用。 jvm系列 垃圾回收基礎 JVM的編譯策略 GC的三大基礎算法 GC的三大高級算法 GC策略的評價指標 JVM信息查看 GC通用日志解讀 jvm的card table數據...
任何程序都需要加載到內存才能與CPU進行交流 同理, 字節碼.class文件同樣需要加載到內存中,才可以實例化類 ClassLoader的使命就是提前加載.class 類文件到內存中 在加載類時,使用的是Parents Delegation Model(溯源委派加載模型) Java的類加載器是一個運行時核心基礎設施模塊,主要是在啟動之初進行類的加載、鏈接、初始化 showImg(https://s...
摘要:驗證驗證是連接階段的第一步,這一階段的目的是為了確保文件的字節流中包含的信息符合當前虛擬機的要求,并且不會危害虛擬機自身的安全。字節碼驗證通過數據流和控制流分析,確定程序語義是合法的符合邏輯的。 看過這篇文章,大廠面試你「雙親委派模型」,硬氣的說一句,你怕啥? 讀該文章姿勢 打開手頭的 IDE,按照文章內容及思路進行代碼跟蹤與思考 手頭沒有 IDE,先收藏,回頭看 (萬一哪次面試問...
閱讀 2185·2021-09-02 15:11
閱讀 1507·2019-08-30 15:43
閱讀 2073·2019-08-29 13:48
閱讀 2790·2019-08-26 13:55
閱讀 2100·2019-08-23 15:09
閱讀 2896·2019-08-23 14:40
閱讀 3421·2019-08-23 14:23
閱讀 2632·2019-08-23 14:20