Docker 的優點
851
2025-04-04
類的生命周期
當java源代碼文件被javac編譯成class文件后,并不能直接運行, 而是需要經過加載,連接和初始化這幾個階段后才能使用。 在使用完類或JVM被銷毀后,JVM會將類卸載掉。
類加載過程需要經過3個階段:
加載
連接
初始化
其中連接又可分為3個階段: 驗證 , 準備 , 解析。
在加載階段,類加載器將類的class文件的二進制數據讀取到內存, 并保存到方法區,并在堆區生成該類的Class對象。
通常有多種方式可以獲取類的二進制數據:
通過javac編譯器編譯java源文件,讀取在本地磁盤上生成的class文件。
從Jar,ZIP等歸檔文件中讀取class文件。
通過網絡讀取類的字節流。
通過動態生成字節碼的技術(如使用動態代理,cglib)來生成class。
PS:數組由數組元素的類型的類加載器在java程序運行時加載,這是ClassLoader類的部分注釋:
見:?測試
1.驗證?驗證階段是為了確保類的字節流符合虛擬機規范,并且不會對虛擬機造成惡意損害。?JVM會對字節流進行如下驗證:
文件格式驗證:會驗證class文件是否符合虛擬機規范,如是否以0×CAFEBABE開頭, 主次版本號是否在虛擬機規定范圍類,常量池中的類型是否有JVM不支持的類型。
元數據驗證: 會對類的元信息進行語義分析,確保符合Java語法規范。
字節碼驗證: 通過分析數據流和控制流,確保類的方法體的程序語義是合法的, 符合邏輯的。
符號引用驗證: 確保常量池中的符號引用能在解析階段正常解析。
2.準備: 準備階段會為類的靜態變量初始化零值,如(0,0L,null,false).
3.解析: 解析階段會將常量池中的符號引用轉為直接引用。 符號引用包括類的全限定名,方法名的描述符,字段名的描述符。直接引用可以簡單理解為目標的內存地址。
初始化階段是類加載過程的最后一個階段。
在這個階段,只有主動使用類才會初始化類,總共有8種情況會涉及到主動使用類。
當jvm執行new指令時會初始化類,即當程序創建一個類的實例對象。
當jvm執行getstatic指令時會初始化類,即程序訪問類的靜態變量(不是靜態常量,常量歸屬于運行時常量池)。
當jvm執行putstatic指令時會初始化類,即程序給類的靜態變量賦值。
當jvm執行invokestatic指令時會初始化類,即程序調用類的靜態方法。
當使用反射主動訪問這個類時,也會初始化類,如Class.forname("..."),newInstance()等等。
當初始化一個子類的時候,會先初始化這個子類的所有父類,然后才會初始化這個子類。
當一個類是啟動類時,即這個類擁有main方法,那么jvm會首先初始化這個類。
MethodHandle和VarHandle可以看作是輕量級的反射調用機制,而要想使用這2個調用, 就必須先使用findStatic/findStaticVarHandle來初始化要調用的類。
PS:見:測試
在類被初始化完成后,就可以使用類了。
類被卸載(Class對象被GC掉)需要滿足3個條件:
該類的實例對象都已被GC,也就是說堆中不存在該類的實例對象。
該類沒有在其它任何地方被使用。
加載該類的類加載器實例已被GC。
在JVM的生命周期中,被JVM自帶的類加載器所加載的類是不會被卸載的。 而被我們自定義的類加載器所加載的類是可能會被卸載的。
其實只要想通一點就好了,jdk自帶的BootstrapClassLoader, PlatformClassLoader和AppClassLoader負責加載jdk提供的類, 它們(類加載器)的實例肯定不會被回收,其中BootstrapClassLoader在java中更是不能被獲取到。 而我們自定義的類加載器的實例是可以被GC掉的, 所以被我們自定義類加載器加載的類是可以被GC掉的。
PS:使用-XX:+TraceClassUnloading 或 -Xlog:class+unload=info可以打印類卸載的信息。
BootstrapClassLoader(用于加載Java基礎核心類庫。由c/c++編寫,Java獲取不到)。
PlatformClassLoader (jdk9之后才有此類加載器,jdk8之前是擴展加載器ExtensionClassLoader 。PlatformClassLoader加載平臺相關的模塊,ExtensionClassLoader加載jdk擴展的模塊)。
AppClassLoader。(應用程序類加載器,負責加載我們程序的classpath下的jar和類)。
自定義類加載器。
每個類加載器實例都有自己的命名空間,命名空間由該加載器及其所有父加載器加載的所有的類組成。
在同一個命名空間中(一個類加載器實例),不會出現全限定名(包括包名)相同的2個類(不會加載2個相同名稱的類)。
在不同的命名空間中(多個類加載器實例),可能會出現全限定名(包括包名)相同的2個類(可能加載2個相同名稱的類)。
PS:見:測試
雙親委派機制是為了防止類被重復加載,避免核心API遭到惡意破壞。?如Object類,它由BootstrapClassLoader在JVM啟動時加載。 如果沒有雙親委派機制,那么Object類就可以被重寫,其帶來的后果將無法想象。
每個類都有其對應的類加載器。
雙親委派機制是指在加載一個類的時候,JVM會判斷這個類是否已經被其類加載器加載過了。 如果已經加載過了,那么直接返回這個類。?如果沒有加載,就使用這個類對應的加載器的父類加載器判斷, 一層一層的往上判斷,最終會由BootstrapClassLoader判斷。?如果BootstrapClassLoader判斷都沒有加載這個類,?那么就由BootstrapClassLoader嘗試加載。 如果BootstrapClassLoader加載失敗了, 就由BootstrapClassLoader的子類加載器們加載。
在jdk9之后,由于模塊化的到來,雙親委派機制也變化了一點: 如果類沒有被加載,那么會根據類名找到這個類的模塊。 如果找到了這個類的模塊, 就由這個類的模塊加載,否則仍然使用父類加載器加載。
可以看出:在加載一個類時,是由下自上判斷類是否被加載的。如果類沒有被加載, 就由上自下嘗試加載類。
Java JVM
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。