1.一個(gè)程序在 JVM 中運(yùn)行的完整流程
說明:
① 類加載器把字節(jié)碼加載到方法區(qū)
② 方法區(qū)的字節(jié)碼被 JVM new,在堆內(nèi)存中生成字節(jié)碼對(duì)象
③ 字節(jié)碼對(duì)象被 GC 的要求有:該類沒有在其他任何地方被引用;該類的所有的實(shí)例對(duì)象都已被 GC;該類的類加載器的實(shí)例已被 GC
2.Java 虛擬機(jī)的生命周期
JVM 的生命周期可以分為啟動(dòng)、運(yùn)行、退出。
(1) 啟動(dòng)
JVM 的啟動(dòng)過程是類加載,通過類加載器把二進(jìn)制字節(jié)碼加載到 JVM。
類加載器采用雙親委派模型, 其類型有 3 種:
① Bootstrap ClassLoader 啟動(dòng)類加載器:負(fù)責(zé)加載系統(tǒng)類和 /lib 目錄的 jar 和類,例如 String
② ExtClassLoader 擴(kuò)展類加載器:負(fù)責(zé)加載 /lib/ext 目錄下的 jar 和類
③ AppClassLoader 應(yīng)用程序類加載器:負(fù)責(zé)加載當(dāng)前應(yīng)用 ClassPath 的 jar 和類
④ UserDefinedClassLoader 用戶自定義加載器:負(fù)責(zé)加載用戶自定義的 jar 和類
示例如下,執(zhí)行 main 方法,需要加載 Test01。根據(jù)雙親委派模型即向上檢查、向下加載,JVM 從 AppClassLoader、ExtClassLoader 到 Bootstrap ClassLoader 檢查和加載 Test01。Test01 是用戶自定義類,Test01 沒有指定父類即默認(rèn)父類是 Object 類,被
AppClassLoader 加載。
public class Test01 {
public static void main(String[] args) {
System.out.println("123");
}
}
類的生命周期:
① 加載:通過全類名獲取該類的二進(jìn)制字節(jié)流、將字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)換為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)、在內(nèi)存中生成一個(gè)代表該類的 Class 對(duì)象(方法區(qū)數(shù)據(jù)的訪問入口);
② 驗(yàn)證:驗(yàn)證文件格式、字節(jié)碼驗(yàn)證、魔數(shù)驗(yàn)證
③ 準(zhǔn)備:分配內(nèi)存給類變量,并設(shè)置類變量初始值。如果是基本數(shù)據(jù)類型,就會(huì)給他們?cè)O(shè)置默認(rèn)值;
④ 解析:將常量池內(nèi)的符號(hào)引用替換為直接引用
⑤ 初始化:new 指令、getstatic 指令、putstatic 指令、invokestatic 指令、 java.lang.reflect 的反射等
⑥ 使用:用戶程序使用
⑦ 卸載:該類的所有的實(shí)例對(duì)象都已被GC、該類沒有在其他任何地方被引用、該類的類加載器的實(shí)例已被GC
(2) 運(yùn)行
一個(gè)JVM 進(jìn)程的運(yùn)行,實(shí)際上是 Java 程序的運(yùn)行。當(dāng)程序運(yùn)行結(jié)束后,JVM 進(jìn)程也就結(jié)束。
JVM 的守護(hù)線程:
Java 有 2 種進(jìn)程,User Thread 用戶線程、Daemon Thread 守護(hù)線程。例如,用戶線程是指用戶自定義的 main 函數(shù),而守護(hù)線程是服務(wù)于用戶線程的,例如 GC 垃圾回收線程。兩種線程的生命周期是一致的,前者優(yōu)先級(jí)高于后者的。
(3)退出
JVM 的退出有多種場(chǎng)景,如下所示。
① 程序正常結(jié)束運(yùn)行
② 程序中調(diào)用 System.exit() 等方法
③ 程序運(yùn)行過程中,遇到異常錯(cuò)誤終止
④ 操作系統(tǒng)、硬件原因?qū)е?JVM 進(jìn)程被結(jié)束
3.Java 程序運(yùn)行的案例

jdk1.7 及以下是方法區(qū),jdk1.8 是元空間 Metaspace