這篇文章主要介紹了JVM運行與類加載的示例分析,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
為什么要使用類加載器?
Java語言里,類加載都是在程序運行期間完成的,這種策略雖然會令類加載時稍微增加一些性能開銷,但是會給java應用程序提供高度的靈活性。例如:
1.編寫一個面向接口的應用程序,可能等到運行時再指定其實現的子類;
2.用戶可以自定義一個類加載器,讓程序在運行時從網絡或其他地方加載一個二進制流作為程序代碼的一部分;(這個是Android插件化,動態安裝更新apk的基礎)
為什么研究類加載全過程?
有助于連接JVM運行過程
更深入了解java動態性(解熱部署,動態加載),提高程序的靈活性
類加載機制
JVM把class文件加載到內存,并對數據進行校驗、解析和初始化,最終形成JVM可以直接使用的java類型的全過程。
加載
將class文件字節碼內容加載到內存中,并將這些靜態數據轉換成方法區中的運行時數據結構,在堆中生成一個代表這個類的java.lang.Class
對象,作為方法區類數據的訪問入口,這個過程需要類加載器參與。
鏈接
將java類的二進制代碼合并到JVM的運行狀態之中的過程
驗證: 確保加載的類信息符合JVM規范,沒有安全方面的問題
準備: 正式為類變量(static變量)分配內存并設置類變量初始值的階段,這些內存都將在方法去中進行分配
解析: 虛擬機常量池的符號引用替換為字節引用過程
初始化
初始化階段是執行類構造器<clinit>()
方法的過程。類構造器<clinit>()
方法是由編譯器自動收藏類中的所有類變量的賦值動作和靜態語句塊(static塊)中的語句合并產生
當初始化一個類的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化
虛擬機會保證一個類的<clinit>()
方法在多線程環境中被正確加鎖和同步
當范圍一個Java類的靜態域時,只有真正聲名這個域的類才會被初始化
例1:
public class Demo01 { public static void main(String[] args) { A a = new A(); System.out.println(a.width); } } class A{ public static int width=100; //靜態變量,靜態域 field static{ System.out.println("靜態初始化類A"); width = 300 ; } public A() { System.out.println("創建A類的對象"); } }
分析:
說明:
內存中存在棧、堆(放創建好的對象)、方法區(實際也是一種特殊堆)
1、JVM加載Demo01時候,首先在方法區中形成Demo01類對應靜態數據(類變量、類方法、代碼…),同時在堆里面也會形成java.lang.Class
對象(反射對象),代表Demo01類,通過對象可以訪問到類二進制結構。然后加載變量A類信息,同時也會在堆里面形成a對象,代表A類。
2、main方法執行時會在棧里面形成main方法棧幀,一個方法對應一個棧幀。如果main方法調用了別的方法,會在棧里面挨個往里壓,main方法里面有個局部變量A類型的a,一開始a值為null,通過new調用類A的構造器,棧里面生成A()方法同時堆里面生成A對象,然后把A對象地址付給棧中的a,此時a擁有A對象地址。
3、當調用A.width時,調用方法區數據。
當類被引用的加載,類只會加載一次
類的主動引用(一定會發生類的初始化)
new一個類的對象
調用類的靜態成員(除了final常量)和靜態方法
使用java.lang.reflect
包的方法對類進行反射調用
當虛擬機啟動,java Demo01,則一定會初始化Demo01類,說白了就是先啟動main方法所在的類
當初始化一個類,如果其父類沒有被初始化,則先初始化它父類
類的被動引用(不會發生類的初始化)
當訪問一個靜態域時,只有真正聲名這個域的類才會被初始化
通過子類引用父類的靜態變量,不會導致子類初始化
通過數組定義類的引用,不會觸發此類初始化
引用常量不會觸發此類的初始化(常量在編譯階段就存入調用類的常量池中了)
例2:
public class Demo01 { static{ System.out.println("靜態初始化Demo01"); } public static void main(String[] args) throws Exception { System.out.println("Demo01的main方法!"); System.out.println(System.getProperty("java.class.path")); //主動引用 // new A(); // System.out.println(A.width); // Class.forName("com.sinosoft.test.A"); //被動引用 // System.out.println(A.MAX); // A[] as = new A[10]; System.out.println(B.width);//B類不會被加載 } } class B extends A { static { System.out.println("靜態初始化B"); } } class A extends A_Father { public static int width=100; //靜態變量,靜態域 field public static final int MAX=100; static { System.out.println("靜態初始化類A"); width=300; } public A(){ System.out.println("創建A類的對象"); } } class A_Father extends Object { static { System.out.println("靜態初始化A_Father"); } }
感謝你能夠認真閱讀完這篇文章,希望小編分享的“JVM運行與類加載的示例分析”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。