【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你懂了嗎,有什么疑問評論區一起交流哦!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM