1. 程式人生 > 其它 >【JVM原理最全、最清晰、最通俗易懂】一次性把JVM講清楚

【JVM原理最全、最清晰、最通俗易懂】一次性把JVM講清楚

JVM 一直都是面試的必考點,大家都知道,但是要把它搞清楚又好像不是特別容易。JVM 的知識點太散,不繫統,所以不便於歸納總結,今天菜菜今天就來幫大家解決這個問題,用一篇文章把 JVM 的結構講清楚。

JVM 可分為 5 個部分,分別是:

1、類載入器(Class Loader)

2、執行時資料區(Runtime Data Area)

3、執行引擎(Execution Engine)

4、本地庫介面(Native Interface)

5、本地方法庫(Native Libraies)

更多視訊學習請點選:

JVM虛擬機器分析視訊教程>>>>>

這其中最複雜的是執行時資料區

,又可分為方法區、虛擬機器棧、本地方法棧、堆、程式計數器,並且方法區和堆是執行緒共享的,虛擬機器棧、本地方法棧、程式計數器是執行緒隔離的,JVM 的結構如下圖所示。

搞清楚了 JVM 虛擬機器的結構,接下來我們詳細講解它的每一部分。

類載入器:載入位元組碼檔案到記憶體。

執行引擎:對 JVM 指令進行解析,翻譯成機器碼,解析完成後提交到作業系統中。

本地庫介面:供 Java 呼叫的融合了不同開發語言的原生庫。

本地方法庫:Java 本地方法的具體實現。

執行時資料區:JVM 核心記憶體空間結構模型。

執行時資料區是 JVM 記憶體結構最重要的部分,接下來我們詳細講解執行時資料區的各個組成部分。

一、方法區

方法區儲存虛擬機器載入的類資訊、常量、靜態變數,即時編譯器編譯後的程式碼等資料。方法區是一種規範,永久代是方法區的一種實現,這裡有個常考的面試題:JDK 7 以前的版本字串常量池是放在永久代中的,JDK 7 將字串常量池移動到了堆中,JDK 8 直接刪除了永久代,改用元空間替代永久代。

二、本地方法棧

本地方法棧與 Java 棧的作用和原理基本相同,都可以用來執行方法,不同點在於 Java 棧執行的是 Java 方法,本地方法棧執行的是本地方法。

什麼是 Java 的本地方法?Java 是基於應用層的高階程式語言,無法訪問作業系統底層資訊,如底層硬體裝置等,這個時候就需要使用其他語言來完成功能了,比如 C 語言,本地方法的使用原理如下所示:

1、在 Java 程式中宣告 native 修飾的方法,只有方法定義,沒有方法實現,將該 Java 檔案編譯成位元組碼檔案。

2、用 javah 編譯位元組碼檔案,生成一個 .h 檔案。

3、寫一個 .cpp 檔案實現 .h 檔案中的方法。

4、將 .cpp 檔案編譯成動態連結庫檔案 .dll 。

5、使用 System.loadLibrary() 載入動態連線庫檔案。

這樣就可以實現本地方法的呼叫,用 Java 呼叫非 Java 編寫的介面,基本原理是利用反射機制,在執行的時候找到 .dll 檔案並且解析,根據動態連結庫中的檔名稱創建出物件和方法,然後我們就可以利用物件呼叫方法了。

常見的本地方法有:public final native Class<?> getClass()、public native int hashCode()、protected native Object clone()。

更多視訊學習請點選:

JVM虛擬機器分析視訊教程>>>>>

三、程式計數器

程式計數器佔用的記憶體空間較小,是當前執行緒所執行的位元組碼行號指示器,通過改變這個計數器的值來選取下一條需要執行的位元組碼指令。多個執行緒之間的程式計數器相互獨立,互不影響,為了保證每個執行緒都恢復後都可以找到具體的執行位置。

四、Java 堆

Java 堆用來存放例項化物件,Java 堆被所有執行緒共享,在虛擬機器啟動時建立,用來存放物件例項,是 Java 記憶體結構中的大頭,佔用大部分的空間,是 GC 的主要管理區域,又可分為年輕代、老年代、永久代,JDK 8 及以後去掉了永久代。

年輕代

年輕代又可分為 Eden,from Survivor,to Survivor。

Eden區:物件剛被建立的時候,存放在 Eden 區,如果 Eden 區放不下,則放在 Survivor 區,甚至老年代中。

Survivor 區:Survivor 又可分為 Survivor From 和 Survivor To,GC 回收時使用,將 Eden 中存活的物件存入 Survior From 中,下一次回收時,將 Survior From 中的物件存入 Survior To 中,清除 Survior From ,下一次回收時重複次步驟,Survior From 變成 Survior To,Survivor To 變成 Survivor From,依次迴圈,同時每次回收,物件的年齡都 +1,年齡增加到一定程度的物件,移動到老年代中。

老年代

存放生命週期較長的物件。JDK 8 之後改用元空間替代永久代。

元空間

Java 8 之後開始將類的元資料放在堆記憶體中,這塊區域叫做元空間,在 Java 7 及以前,元空間是放在永久代中的,Java 8 之後分離出來了。

元空間和永久代是方法區的實現,方法區只是一種規範,在 Java 7 之後,原先位於方法區永久代裡的字串常量池已被移動到了 Java 堆中,因為永久代的記憶體空間極為有限,如果頻繁呼叫 inter 方法,記憶體無法儲存這麼多資料。在 Java 8 之後將永久代完全刪除了,使用元空間替代了永久代。

元空間使用本地記憶體,永久代使用 JVM 記憶體,所以使用元空間的好處在於程式的記憶體不在受限於 JVM 記憶體,本地記憶體剩餘多少空間,元空間就可以有多大,解決了空間不足的問題。

五、虛擬機器棧

Java 方法執行的記憶體模型,Java 棧中存放的是多個棧幀,每個棧幀對應一個被呼叫的方法,主要包括區域性變量表、運算元棧、動態連結、方法返回地址(方法出口)。每一個方法的執行,JVM 都會建立一個棧幀,並且將棧幀壓入 Java 棧,方法執行完畢,該棧幀出棧。

區域性變量表:儲存方法執行過程中的所有變數,包括方法中宣告的區域性變數和形參。

運算元棧:方法中的計算過程都是藉助於運算元棧來完成的,將參與計算的資料壓入運算元棧。

棧的具體運算方式是這樣的,編譯器是通過兩個棧來實現的,一個是儲存運算元的棧,另一個是儲存運算子的棧。我們從左向右遍歷表示式,當遇到數字,直接壓入運算元棧。當遇到運算子,先與運算子棧的棧頂元素進行比較,如果高於當前棧頂元素的優先順序,直接壓入,否則取出當前棧頂的運算子,同時取出運算元棧的前兩個資料進行運算,並將結果壓入運算元棧。再次重複上述步驟,直到當前的運算子被壓入棧中,當沒有新的運算子需要入棧的時候,取出當前的棧頂元素以及運算元棧的兩個運算,進行運算,將結果壓入運算元棧,如果方法定義時需要返回值,直接將運算元棧棧頂元素返回即可。

方法返回地址:一個方法呼叫結束之後要返回到呼叫它的地方,所以棧幀中要保持能夠返回到方法呼叫處的地址。

每個執行緒都有自己的 Java 棧,相互獨立,可以同時執行各種的方法,每個方法的執行都是一個棧幀的入棧和出棧過程,Java 虛擬機器棧用來儲存棧幀,方法呼叫結束之後,幀會被銷燬。

更多視訊學習請點選:

JVM虛擬機器分析視訊教程>>>>>

簡單易懂的講解了jvm你懂了嗎,有什麼疑問評論區一起交流哦!