前言:學習JVM是一個漫長的課程,在你學的過程中,JVM的學習過程中是枯燥的乏味的,不要想着一口喫一個胖子,需要沉下心,一步一步來。
JVM是一個令人望而卻步的領域,因爲它博大精深,涉及到的內容與知識點非常之多。雖然Java開發者每天都在使用JVM,但對其有所研究並且研究深入的人卻少之又少。然而,JVM的重要性卻又是不言而喻的。基於JVM的各種動態與靜態語言生態圈已經異常繁榮了,對JVM的運行機制有一定的瞭解不但可以提升我們的競爭力,還可以讓我們在面對問題時能夠沉着應對,加速問題的解決速度;同時還能夠增強我們的自信心,讓我們更加遊刃有餘。
本人也是剛學JVM,想要把JVM學好,最起碼能夠應付面試和讓自己對程序的理解更上一個臺階。我這次打算從jvm的以下方面進行學習:
- JVM介紹
- HotSpot虛擬機講解
- 垃圾收集方式詳解
- 垃圾收集算法詳解
- 垃圾收集器詳解
- 分代垃圾收集機制詳解
- 新生代講解
- 老年代講解
- G1收集器分析與實例
- 常見且重要虛擬機參數示例
- 棧
- 方法區
- 線程共享內存區
- 根搜索算法
- Serial收集器
- ParNew收集器
- 類加載機制詳解
- 類加載的雙親委託機制
- 字節碼文件生成與分析
- 魔數
- 常量池與方法表
- 各種指令詳解
- 鎖詳解
- 線程安全
- 偏向鎖、自旋鎖與輕量級鎖
- JIT編譯器
- GC日誌生成與分析
- 虛擬機監控工具詳解
- jConsole使用方式詳解
- 何爲逃逸與逃逸分析
- 方法內聯
- 虛擬機內存模型詳解
這些東西在深入理解Java虛擬機上面其實是都有用的,但是我不會按照上面羅列的來介紹。我會按照我自己的理解來完善我這個系列博客的。
1.類加載
在java的代碼中,類型的加載,連接,初始化的過程都是在程序運行期間完成的
加載:查找並加載二進制數據
連接:
驗證:確保加載類的正確性
準備:爲類的靜態變量分配內存,將其初始化爲默認值
解析:把類中的符號引用轉化爲直接引用
初始化:爲類的靜態變量賦予正確的初始值
類加載的五個階段:
java程序對類的使用方式有兩種,主動使用和被動使用
所有的java虛擬機實現必須在每個類或者接口被Java程序“首次主動使用”時才初始化他們
何爲主動使用(七種方式):
1.創建類的實例
2.訪問某個類或者接口的靜態變量,或者對該靜態變量賦值
3.調用類的靜態方法
4.反射
5.初始化一個類的子類
6.java虛擬機啓動被標明啓動類的類
7.jdk1.7開始提供的動態語言支持(瞭解)
除了這七種情況,其他使用java類的方式都被看做是對類的被動使用,都不會導致類的初始化
舉一個例子來分析下啥是類的主動和被動加載
下面我們可以把子類的靜態變量改爲靜態常量
我們可以看出來,這一次並沒有執行父類的靜態構造,這是爲什麼呢???
原因:在編譯階段,把final定義的常量放到了調用這個常量的方法所在類的常量池中(這裏就放在了MyTest的常量池當中)
我們下面把代碼稍微的改東下,我們再看下輸出的結果
原因:當一個常量的值並非編譯期間能夠確定的,那麼其值就不會放到調用類的常量池中,這是程序在運行的時候,會導致主動使用這個常量所在的類,會導致這個類被初始化
下面我們來分析下父子類接口的加載關係:
首先我們要先把結論說出來:當一個接口被初始化的時候,並不要求他的父類完成初始化,這一點是和類是不一樣的
爲什麼呢:這個案例其實不是很好舉的,你在接口的定義的變量其實都是被final修飾的,他們都會被放到調用類的常量池裏面,從而不會加載父類接口。
我們下面來分析一下準備階段和初始化順序的問題(面試的時候,筆試題可能會有這種類型的題,很坑哦)
我們來根據類的準備和初始化階段來分析下這段代碼的執行順序
這一切的結果應該和很多人的想法一致,那麼我們現在把,Single類裏面的b的定義放在構造方法下面,我們再來分析一下這個結果和執行情況
有兩種類型的類加載器
Java虛擬機自帶的加載器
根類加載器(Bootstrap):該加載器沒有父加載器,它負責加載虛擬機中的核心類庫。根類加載器從系統屬性sun.boot.class.path所指定的目錄中加載類庫。類加載器的實現依賴於底層操作系統,屬於虛擬機的實現的一部分,它並沒有集成java.lang.ClassLoader類。
擴展類加載器(Extension):它的父加載器爲根類加載器。它從java.ext.dirs系統屬性所指定的目錄中加載類庫,或者從JDK的安裝目錄的jre\lib\ext子目錄(擴展目錄)下加載類庫,如果把用戶創建的jar文件放在這個目錄下,也會自動由擴展類加載器加載,擴展類加載器是純java類,是java.lang.ClassLoader的子類。
系統應用類加載器(System):也稱爲應用類加載器,它的父加載器爲擴展類加載器,它從環境變量classpath或者系統屬性java.class.path所指定的目錄中加載類,他是用戶自定義的類加載器的默認父加載器。系統類加載器時純java類,是java.lang.ClassLoader的子類。
用戶自定義的類加載器
java.lang.ClassLoader的子類
用戶可以定製類的加載方式
根類加載器–>擴展類加載器–>系統應用類加載器–>自定義類加載器
類加載器並不需要等到某個類被“首次主動使用”時再加載它
JVM規範允許類加載器在預料某個類將要被使用時就預先加載它,如果在預先加載的過程中遇到了.class文件缺失或存在錯誤,類加載器必須在程序首次主動使用該類才報告錯誤(LinkageError錯誤),如果這個類沒有被程序主動使用,那麼類加載器就不會報告錯誤。
類加載器用來把類加載到java虛擬機中。從JDK1.2版本開始,類的加載過程採用父親委託機制,這種機制能更好地保證Java平臺的安全。在此委託機制中,除了java虛擬機自帶的根類加載器以外,其餘的類加載器都有且只有一個父加載器。當java程序請求加載器loader1加載Sample類時,loader1首先委託自己的父加載器去加載Sample類,若父加載器能加載,則有父加載器完成加載任務,否則才由加載器loader1本身加載Sample類。
由於本人工作的原因,最近java原理這個專題可能要延後一階段才能更新,但是java原理這個章節的內容我肯定會慢慢完善的。