- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) >
- java中Class類(lèi)裝載流程是怎樣的
java中Class類(lèi)裝載流程是怎樣的,很多新手對此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細講解,有這方面需求的人可以來(lái)學(xué)習下,希望你能有所收獲。
java虛擬機加載class文件過(guò)程如下:
class文件只有在必須要使用的時(shí)候才會(huì )被裝載,java虛擬機不會(huì )無(wú)條件地裝載class類(lèi)型。java虛擬機規定,一個(gè)類(lèi)或接口在初次使用前,必須進(jìn)行初始化。這里的“使用”是指主動(dòng)使用,主動(dòng)使用只有下列幾種情況:
當創(chuàng )建一個(gè)類(lèi)的實(shí)例時(shí),比如使用new
關(guān)鍵字或反射、克隆、反序列化。
當調用類(lèi)的靜態(tài)方法時(shí),使用了字節碼invokestatic
指令。
當使用類(lèi)或接口的靜態(tài)字段(final
常量除外),比如使用getstatic
或者putstatic
指令。
當使用java.lang.reflect
包中的方法反射類(lèi)的方法時(shí)。
當初始化子類(lèi)時(shí),要求先初始化父類(lèi)。
作為啟動(dòng)虛擬機,含有main()
方法的那個(gè)類(lèi)。
public class Parent { static { System.out.println("Parent init"); } public static int v = 100; } public class Child extends Parent { static { System.out.println("Child init"); } } public class Demo01 { public static void main(String[] args) { Child c = new Child(); } }
以上代碼聲明了3個(gè)類(lèi):Parent
、Child
和Demo01
,Child
為Parent
的子類(lèi)。若Parent
被初始化,由代碼中的static
語(yǔ)句塊可知,將會(huì )打印“Parent Init
”,若Child
被初始化,則會(huì )打印“Child Init
”,執行Demo01
,運行結果:
Parent init Child init
由此可知,系統首先裝載Parent
類(lèi),接著(zhù)裝載Child
類(lèi)。
public class Parent { static { System.out.println("Parent init"); } public static int v = 100; } public class Child extends Parent { static { System.out.println("Child init"); } } public class Demo02 { public static void main(String[] args) { System.out.println(Child.v); } }
查看以上代碼,Parent
中有靜態(tài)變量v,并且在Demo02
中,使用其子類(lèi)Child
調用父類(lèi)中的變量。運行以上代碼,輸出結果如下:
Parent init 100
可以看到,雖然在Demo02
中直接訪(fǎng)問(wèn)了子類(lèi)對象,但是Child
子類(lèi)并未被初始化,只有Parent
父類(lèi)被初始化??梢?jiàn),在使用一個(gè)字段時(shí),只有直接定義該字段的類(lèi)才會(huì )被初始化。
注意:雖然Child類(lèi)沒(méi)有被初始化,但是此時(shí)Child類(lèi)已經(jīng)被系統加載,只是沒(méi)有進(jìn)入初始化階段。
使用-XX:+TraceClassLoading
參數運行這段代碼,就會(huì )得到以下日志(刪除部分輸出):
oaded java.net.Inet6Address from /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/rt.jar] [Loaded java.net.Inet6Address$Inet6AddressHolder from /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/rt.jar] [Loaded jvm.chapter10.Parent from file:/Users/chengyan/IdeaProjects/myproject/DataStructuresAndAlgorithms/out/production/DataStructuresAndAlgorithms/] [Loaded jvm.chapter10.Child from file:/Users/chengyan/IdeaProjects/myproject/DataStructuresAndAlgorithms/out/production/DataStructuresAndAlgorithms/] Parent init 100
加載類(lèi)處于類(lèi)裝載的第一個(gè)階段。在加載類(lèi)時(shí),java虛擬機必須完成以下工作:
通過(guò)類(lèi)的全名獲取類(lèi)的二進(jìn)制數據源
解析類(lèi)的二進(jìn)制數據流為方法區內的數據結構
創(chuàng )建java.lang.Class類(lèi)的實(shí)例,表示該類(lèi)型
對于類(lèi)的二進(jìn)制數據流,虛擬機可能通過(guò)多種途徑產(chǎn)生或獲得,如:class文件,或者jar、zip等歸檔數據包,也可以通過(guò)網(wǎng)絡(luò )加載。
在獲取類(lèi)的二進(jìn)制數據后,java虛擬機就會(huì )處理這些數據,并最終轉為一個(gè)java.lang.Class
類(lèi)提供的實(shí)例。
當類(lèi)加載到系統后,就開(kāi)始連續操作,驗證是連續操作的第一步。它的目的是保存加載的字節碼是合法、合理并且符合規范的。驗證的步驟比較復雜,實(shí)際要驗證的項目也繁多,大體上java虛擬機需要做的檢查如下:
當一個(gè)類(lèi)驗證難過(guò)時(shí),虛擬機就會(huì )進(jìn)入準備階段。在這個(gè)階段,虛擬機會(huì )為這個(gè)類(lèi)分配相應的內存空間,并設置初始值。java虛擬機各類(lèi)型變量默認值的初始值如下:
注意:java并不支持boolean類(lèi)型,對于boolean類(lèi)型,內部實(shí)現實(shí)際上是int類(lèi)型,由于int類(lèi)型的默認值是0,故對應的boolean類(lèi)型的默認值就是false.
如果類(lèi)存在常量字段(final修飾的字段),那么常量字段也會(huì )在準備階段被賦值上正確的值,這個(gè)賦值屬于java虛擬機的行為,屬于變量的初始化。
準備階段后是解析階段。解析階段的工作就是將類(lèi)、接口、字段和方法的符號引用轉為直接引用。
符號引用就是一些字面量的引用,和虛擬機的內部數據結構與內存布局無(wú)關(guān)。在Class類(lèi)文件中,在大量的符號引用,但在運行時(shí),只有符號引用是不夠的,系統需要明確變量、方法等的位置。以方法為例,java虛擬機為每個(gè)類(lèi)都準備了一張方法表,將其所有的方法都列在表中,當需要調用一個(gè)類(lèi)的方法時(shí),只要知道這個(gè)方法在方法表中的偏移量就可以直接調用。通過(guò)解析操作,符號引用可以轉變?yōu)槟夸浄椒ㄔ陬?lèi)的方法表中的位置,從而使得方法被成功調用。
綜上所述,所謂解析,就是將符號引用轉為直接引用,也就是得到類(lèi)或者字段、方法在內存中的指針或者偏移量。
類(lèi)的初始化是類(lèi)裝載的最后一個(gè)階段,如果前面的步驟都沒(méi)有問(wèn)題,表示類(lèi)可以順利裝載到系統中。此時(shí),類(lèi)才會(huì )開(kāi)始執行java字節碼。初始化階段的重要工作是執行類(lèi)的初始化方法 <clinit>
。方法<clinit>
是由編譯器自動(dòng)生成的,它是由靜態(tài)成員的賦值語(yǔ)句及static
語(yǔ)句塊共同產(chǎn)生的。
值得一提的是,對于<clinit>
方法,也就是類(lèi)的初始化,虛擬機會(huì )在內部確保其多線(xiàn)程環(huán)境中的安全性。也就是說(shuō),當多個(gè)線(xiàn)程試圖初始化同一個(gè)類(lèi)時(shí),只有一個(gè)線(xiàn)程可以進(jìn)入<clinit>
方法了(當需要使用這個(gè)類(lèi)時(shí),虛擬機就會(huì )直接返回它已經(jīng)準備好的信息)。
正因為<clinit>
方法是帶鎖線(xiàn)程安全的,所以在多線(xiàn)程環(huán)境下進(jìn)行類(lèi)初始化時(shí),可能會(huì )引起死鎖,并且這種死鎖是很難發(fā)現,因為看起來(lái)他們并沒(méi)有可用的鎖信息。
下面的代碼展示了在類(lèi)的初始化時(shí),產(chǎn)生了死鎖線(xiàn)程:
package jvm.chapter10; class StaticA { static { try { Thread.sleep(1000); } catch (Exception e) { } try { Class.forName("jvm.chapter10.StaticB"); } catch (ClassNotFoundException e) { } System.out.println("StaticA init OK!"); } } class StaticB { static { try { Thread.sleep(1000); } catch (Exception e) { } try { Class.forName("jvm.chapter10.StaticA"); } catch (Exception e) { } System.out.println("StaticB init OK"); } } public class Demo03 extends Thread { private char flag; public Demo03(char flag) { this.flag = flag; } @Override public void run() { try { Class.forName("jvm.chapter10.Static" + flag); } catch (Exception e) { } System.out.println(getName() + "over"); } public static void main(String[] args) throws Exception { Demo03 loadA = new Demo03('A'); Demo03 loadB = new Demo03('B'); loadA.start(); loadB.start(); loadA.join(); loadB.join(); } }
以上代碼由3個(gè)類(lèi)組成:StaticA
、StaticB
和 Demo03
.在Demo03
中創(chuàng )建了兩個(gè)線(xiàn)程,線(xiàn)程A
試圖初始化StaticA
,線(xiàn)程B
嘗試初始化StaticB
,在StaticA
的初始化過(guò)程中,會(huì )嘗試初始化StaticB
,同樣在StaticB
的初始化過(guò)程中,也初始化 StaticA
,這就導致了死鎖。
通過(guò)線(xiàn)程堆棧dump可以得到如下信息:
"Thread-1" #12 prio=5 os_prio=31 tid=0x00007f9dd4853000 nid=0x5703 in Object.wait() [0x0000700004797000] java.lang.Thread.State: RUNNABLE at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:264) at jvm.chapter10.StaticB.<clinit>(Demo03.java:33) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:264) at jvm.chapter10.Demo03.run(Demo03.java:52) Locked ownable synchronizers: - None "Thread-0" #11 prio=5 os_prio=31 tid=0x00007f9dd4801800 nid=0xa803 in Object.wait() [0x0000700004694000] java.lang.Thread.State: RUNNABLE at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:264) at jvm.chapter10.StaticA.<clinit>(Demo03.java:17) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:264) at jvm.chapter10.Demo03.run(Demo03.java:52) Locked ownable synchronizers: - None
在這種情況下,系統并沒(méi)有給出足夠的信息來(lái)判定死鎖,但是死鎖確實(shí)存在,我們需要格外小心由類(lèi)的初始化引起的死鎖問(wèn)題。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站