一文幫你理清JVM裡面的彎彎繞繞

每一個Java 開發人員都知道字節碼由JRE (Java運行時環境)執行。但許多人不知道JRE是Java虛擬機(JVM)的實現, 它負責分析字節碼、解析並執行代碼。作為一個開發人員瞭解JVM架構是非常重要的,因為它使我們能更高效的編寫代碼。在這篇文章中我們將更深入瞭解Java中的JVM架構以及JVM的各個組件。

那麼JVM到底是什麼呢?

虛擬機 是物理機器的一個軟件實現。Java運行在VM上,實現WORA (一處編寫,處處運行)。 編譯器將Java文件編譯成Java .class 文件,然後這個.class文件被輸入到JVM中進行類文件的加載和執行。下面是一個JVM的架構圖。

一文幫你理清JVM裡面的彎彎繞繞

JVM是如何工作的呢?

正如上面的架構圖所示,JVM被分為三個主要的子系統:

類加載器子系統

運行時數據區

執行引擎

1. 類加載器子系統

Java的動態類加載功能是由類加載器子系統處理。當它在運行時(不是編譯時)首次引用一個類時,它加載、鏈接並初始化該類文件。

1.1 加載

類由此組件加載。啟動類加載器 (Boot Strap class Loader)、擴展類加載器(Extension class Loader)和應用程序類加載器(Application class Loader) 這三種類加載器幫助完成類的加載。

啟動類加載器 – 負責從啟動類路徑中加載類,無非就是rt.jar。這個加載器會被賦予最高優先級。

擴展類加載器 – 負責加載ext 目錄(jre\\lib)內的類.

應用程序類加載器 – 負責加載應用程序級別類路徑,涉及到路徑的環境變量等etc.

上述的類加載器會遵循委託層次算法(Delegation Hierarchy Algorithm)加載類文件。

1.2 鏈接

校驗 – 字節碼校驗器會校驗生成的字節碼是否正確,如果校驗失敗,我們會得到校驗錯誤。

準備 – 分配內存並初始化默認值給所有的靜態變量。

解析 – 所有符號內存引用被方法區(Method Area)的原始引用所替代。

1.3 初始化

這是類加載的最後階段,這裡所有的靜態變量會被賦初始值, 並且靜態塊將被執行。

2. 運行時數據區(Runtime Data Area)

運行時數據區域被劃分為5個主要組件:

方法區(Method Area)

– 所有類級別數據將被存儲在這裡,包括靜態變量。每個JVM只有一個方法區,它是一個共享的資源。

堆區(Heap Area)– 所有的對象和它們相應的實例變量以及數組將被存儲在這裡。每個JVM同樣只有一個堆區。由於方法區和堆區的內存由多個線程共享,所以存儲的數據不是線程安全的。

棧區(Stack Area)– 對每個線程會單獨創建一個運行時棧。對每個函數呼叫會在棧內存生成一個棧幀(Stack Frame)。所有的局部變量將在棧內存中創建。棧區是線程安全的,因為它不是一個共享資源。棧幀被分為三個子實體:

1.局部變量數組– 包含多少個與方法相關的局部變量並且相應的值將被存儲在這裡。

2.操作數棧– 如果需要執行任何中間操作,操作數棧作為運行時工作區去執行指令。

3.幀數據– 方法的所有符號都保存在這裡。在任意異常的情況下,catch塊的信息將會被保存在幀數據裡面。

4.PC寄存器– 每個線程都有一個單獨的PC寄存器來保存當前執行指令的地址,一旦該指令被執行,pc寄存器會被更新至下條指令的地址。

5.本地方法棧– 本地方法棧保存本地方法信息。對每一個線程,將創建一個單獨的本地方法棧。

3. 執行引擎

分配給運行時數據區的字節碼將由執行引擎執行。執行引擎讀取字節碼並逐段執行。

解釋器– 解釋器能快速的解釋字節碼,但執行卻很慢。 解釋器的缺點就是,當一個方法被調用多次,每次都需要重新解釋。

JIT 編譯器– JIT編譯器消除了解釋器的缺點。執行引擎利用解釋器轉換字節碼,但如果是重複的代碼則使用JIT編譯器將全部字節碼編譯成本機代碼。本機代碼將直接用於重複的方法調用,這提高了系統的性能。

1.中間代碼生成器– 生成中間代碼

2.代碼優化器– 負責優化上面生成的中間代碼

3.目標代碼生成器– 負責生成機器代碼或本機代碼

4.探測器(Profiler)– 一個特殊的組件,負責尋找被多次調用的方法。

3.垃圾回收器: 收集並刪除未引用的對象。可以通過調用"System.gc()"來觸發垃圾回收,但並不保證會確實進行垃圾回收。JVM的垃圾回收只收集哪些由new關鍵字創建的對象。所以,如果不是用new創建的對象,你可以使用finalize函數來執行清理。

此處敲黑板

一文幫你理清JVM裡面的彎彎繞繞


一文幫你理清JVM裡面的彎彎繞繞

關注+轉發 私信“Java”獲取面試資料

關注+轉發 私信“Java”獲取面試資料

關注+轉發 私信“Java”獲取面試資料

JVM內存模型


一文幫你理清JVM裡面的彎彎繞繞

由顏色可以看出,jdk1.8之前,堆內存被分為新生代,老年代,永久帶,jdk1.8及以後堆內存被分成了新生代和老年代。新生代的區域又分為eden區,s0區,s1區,默認比例是8:1:1,元空間可以理解為直接的物理內存

一文幫你理清JVM裡面的彎彎繞繞

程序員到底要不要學習JVM

總有人問這個東西好像用不上,於是要不要學這樣的問題。

然後又總有人擔心一直搬磚成天做些重複沒提升的東西。

如果你這輩子只甘心做一個平庸的Java碼農,那麼你完全沒有必要去學習JVM相關的知識,學習JVM對於一個Java程序員的好處大概可以概括為下幾點:

1.你能夠明白為什麼Java最早期被稱為解釋型語言,而後來為什麼又被大家叫做解釋與編譯並存的語言(瞭解JVM中解釋器以及即時編譯器就可以回答這個問題);

2.你能夠理解動態編譯與靜態編譯的區別,以及動態編譯相對於靜態編譯到底有什麼好處(JVM JIT);

3.你能夠利用一些工具,jmap, jvisualvm, jstat, jconsole等工具可以輔助你觀察Java應用在運行時堆的佈局情況,由此你可以通過調整JVM相關參數提高Java應用的性能;

4.可以清楚知道Java程序是如何執行的;

5.可以明白為什麼Java等高級語言具有可移植性強的特性。

其實這個問題相當於“為什麼C/C++程序員需要學體系結構與編譯原理?”

JVM暫時總結到這裡,下次和大家掰扯掰扯JVM如何調優,先放張思維導圖

一文幫你理清JVM裡面的彎彎繞繞

BATJ關於JVM面試題

一文幫你理清JVM裡面的彎彎繞繞

一文幫你理清JVM裡面的彎彎繞繞

再次敲黑板

需要面試資料

關注+轉發 私信“Java”免費獲取


一文幫你理清JVM裡面的彎彎繞繞



分享到:


相關文章: