類的生命周期
首先我們先看類的生命周期
類的加載程序包含了加載、驗證、準備、決議、初始這五個階段,其中除了決議階段其他四個階段的發生順序都是確定的,因為決議階段在某些情況下會在初始階段之后開始,同時這些階段都是按順序開始的不是按順序進行或結束,因為這些階段通常都是互相交叉的混合進行,以下為類的生命周期
加載->驗證->準備->決議->初始化->使用->卸載 (驗證->準備->決議這三個可概括為連接階段)
在類的的加載階段,虛擬機需要完成三件事:1、 通過一個類的全限定名來獲取其定義的二進制位元組流,2、將這個位元組流所代表的金泰存盤結構轉化為方法區的運行時資料結構,3、Java堆中生成一個代表這個類的Java.lang.Class物件,作為對方法區中這些資料的訪問入口,
類加載器并不需要等到某個類被“首次主動使用”時再加載它,JVM規范允許類加載器在預料某個類將要被使用時就預先加載它,如果在預先加載的程序中遇到了.class檔案缺失或存在錯誤,類加載器必須在程式首次主動使用該類時才報告錯誤(LinkageError錯誤)如果這個類一直沒有被程式主動使用,那么類加載器就不會報告錯誤,
連接階段:
首先進行驗證,確保當前Class檔案的位元組流資訊是否符合當前虛擬機的要求分為:檔案格式驗證、元資料驗證、位元組碼驗證、 符號參考驗證,然后進行準備階段開始正式為類的變數(僅包括類變數static,不包含實體變數,實體變數會在物件實體化時一起分配到Java堆中)分配記憶體并設定變數初始值(零值非代碼中的數值)的階段,這些記憶體在方法區中分配,開始決議,將常量池中的常量參考替換為直接參考,
初始化為類的靜態變數賦予正確的初始值,
初始化有以下步驟:如果這個類還沒有被加載和連接則先進行加載和連接;如果該類 的父類還沒有被初始化,則先初始化其父;如果該類中有初始化陳述句則先依次執行類中的初始化陳述句,
由此也可得出類的初始話時機,創建類的實體時(new);訪問某個類或介面的靜態變數或對該靜態變數賦值時;呼叫類的靜態方法時;反射(Class.forname("com.XXX.XXX"));初始化某個類的子類時;Java虛擬機啟動時被標記為啟動類時
使用:類訪問方法區內的資料結構的介面,物件時Heap區(堆區)的資料,
最后卸載階段:執行了System.exit()方法時;程式正常結束時;程式執行程序中遇到了例外或錯誤導致例外終止;由于作業系統出翔錯誤而導致java虛擬機行程終止;
類加載器、jvm加載機制:
再java程式員的眼中類加載器主要分為三種;
啟動類加載器: Bootstrap ClassLoader,負責加載存放在JDK\jre\lib(JDK代表JDK的安裝目錄,下同)下,或被-Xbootclasspath引數指定的路徑中的,并且能被虛擬機識別的類別庫(如rt.jar,所有的java.*開頭的類均被Bootstrap ClassLoader加載),啟動類加載器是無法被Java程式直接參考的,
擴展類加載器: Extension ClassLoader,該加載器由sun.misc.Launcher$ExtClassLoader實作,它負責加載JDK\jre\lib\ext目錄中,或者由java.ext.dirs系統變數指定的路徑中的所有類別庫(如javax.*開頭的類),開發者可以直接使用擴展類加載器,
應用程式類加載器: Application ClassLoader,該類加載器由sun.misc.Launcher$AppClassLoader來實作,它負責加載用戶類路徑(ClassPath)所指定的類,開發者可以直接使用該類加載器,如果應用程式中沒有自定義過自己的類加載器,一般情況下這個就是程式中默認的類加載器,
類的加載:
-
命令列啟動應用時候由JVM初始化加載
-
通過Class.forName()方法動態加載
-
通過ClassLoader.loadClass()方法動態加載
Class.forName()和ClassLoader.loadClass()區別?
-
Class.forName(): 將類的.class檔案加載到jvm中之外,還會對類進行解釋,執行類中的static塊;
-
ClassLoader.loadClass(): 只干一件事情,就是將.class檔案加載到jvm中,不會執行static中的內容,只有在newInstance才會去執行static塊,
-
Class.forName(name, initialize, loader)帶參函式也可控制是否加載static塊,并且只有呼叫了newInstance()方法采用呼叫建構式,創建類的物件 ,
JVM類加載機制:
-
全盤負責,當一個類加載器負責加載某個Class時,該Class所依賴的和參考的其他Class也將由該類加載器負責載入,除非顯示使用另外一個類加載器來載入 -
父類委托,先讓父類加載器試圖加載該類,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類 -
快取機制,快取機制將會保證所有加載過的Class都會被快取,當程式中需要使用某個Class時,類加載器先從快取區尋找該Class,只有快取區不存在,系統才會讀取該類對應的二進制資料,并將其轉換成Class物件,存入快取區,這就是為什么修改了Class后,必須重啟JVM,程式的修改才會生效 -
雙親委派機制, 如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把請求委托給父加載器去完成,依次向上,因此,所有的類加載請求最終都應該被傳遞到頂層的啟動類加載器中,只有當父加載器在它的搜索范圍中沒有找到所需的類時,即無法完成該加載,子加載器才會嘗試自己去加載該類,
雙親委派機制程序?
-
當AppClassLoader加載一個class時,它首先不會自己去嘗試加載這個類,而是把類加載請求委派給父類加載器ExtClassLoader去完成,
-
當ExtClassLoader加載一個class時,它首先也不會自己去嘗試加載這個類,而是把類加載請求委派給BootStrapClassLoader去完成,
-
如果BootStrapClassLoader加載失敗(例如在$JAVA_HOME/jre/lib里未查找到該class),會使用ExtClassLoader來嘗試加載;
-
若ExtClassLoader也加載失敗,則會使用AppClassLoader來加載,如果AppClassLoader也加載失敗,則會報出例外ClassNotFoundException,
雙親委派優勢
-
系統類防止記憶體中出現多份同樣的位元組碼
-
保證Java程式安全穩定運行
-
JVM記憶體結構:
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/534127.html
標籤:其他
上一篇:day01-Tomcat框架分析
