程序員進階系列:多圖教你掌握JVM

說起 JVM 這個詞,估計你們 都能簡單說兩句,可是根據身邊朋友以及諸多粉絲提出的疑問, 能系統講出來 JVM 的 卻真心不多。

網上講解 JVM 這塊的文章很是多,不過魚龍混雜,鑑於 JVM 也是最考驗 Java 程序員的基礎功底啦,今天靜下來,一塊兒畫畫圖,一塊兒梳理梳理,好好填補一下這塊,爭取不管走到哪裏,你們在腦海中都能有行走的 JVM 內存模型圖。java

1程序員

 JVM 初識   

在講解 JVM 以前,先來揭祕一下 Java 程序是如何實現一次編譯處處運行的?json

步驟一:用文本編輯器或者 IDE,快速編寫 HelloWorld.java 的源代碼文件;
微信

步驟二:用 Java 編譯器(javac)把源代碼(*.java)編譯成字節碼文件(*.class);網絡

步驟三:字節碼文件(.class)即可以在任何安裝了 JVM 的操做系統中運行,JVM 會將字節碼翻譯成能夠被機器執行的本地機器碼。架構

那麼重點來了,Java 是如何實現一次編譯處處運行的呢?經過上圖應該很清晰找到解。app

解一:Java 一次編譯,處處運行,跨平臺的特性是經過 JVM 來實現的,經過 JVM 來屏蔽底層操做系統的差別;
框架

解二:Java 經過 JVM 來實現跨平臺,可是 JVM 是不跨平臺的,也就是說不一樣操做系統之上的 JVM 是不一樣的,Linux 系統上的 JVM 不能用在 Windows 系統上。編輯器

2
函數

 JVM 窺探 

既然已經知曉 Java 程序能夠經過 JVM 來實現一次編譯,處處運行的跨平臺特性,那麼 JVM 究竟是什麼呢?

JVM 是 Java Virtual Machine(Java虛擬機)的縮寫,JVM 是一種用於計算設備的規範,它是一個虛構出來的計算機,是經過在實際的計算機上仿真模擬各類計算機功能來實現的 —— 百度百科。

上面是引了一段百度百科對 JVM 的解釋,大意就是 Java 虛擬機是在計算機上虛構出來的一個計算機,既然是虛構的就意味着看不到,只在於內存之中。

(圖片來源於網絡)

如上圖所示,計算機主要有運算器、控制器、內外存儲器、輸入和輸出設備組成,那麼 JVM 結構長啥樣子呢?

仍是以開篇的 HelloWorld 爲例,窺探一下 JVM 的運行流程

1. Java 源代碼文件會被 Java 編譯器編譯爲字節碼文件(.class);

2. JVM 中的類加載器進行加載各個類的字節碼文件,將全部類結構和方法變量放入運行時數據區;

3. 字節碼文件加載完畢以後,交給 JVM 執行引擎進行執行。

JVM = 類加載器 ClassLoader + 執行引擎 Execution Engine + 運行時數據區域 Runtime Data Area。


在程序執行過程當中,會分配內存空間來存儲程序執行期間須要用到的數據相關信息,分配的內存空間被稱做爲 Runtime Data Area(運行時數據區),也就是常說的 JVM 內存,接下來就重點說一說 JVM 運行時數據區。


(JDK 1.7 內存模型)


如上圖所示,JVM 運行時數據區主要分爲程序計數器、虛擬機棧、本地方法棧、方法區、堆。


如上圖所示,按照內存共享來劃分 JVM 內存,主要劃分爲線程共享內存區域(堆、方法區)、線程私有內存區域(程序計數器、虛擬機棧、本地方法棧)、直接內存。

3

 JVM 運行時數據區解剖 

從上面介紹,能夠清晰知道 JVM 運行時數據區,主要分爲程序計數器、虛擬機棧、本地方法棧、方法區、堆。接下來就逐步解剖,簡單瞭解一下。


1. 程序計數器


程序計數器(PC Register),也有人稱它爲 PC 寄存器。


要是爲了可以使得每一個線程都在線程切換後可以恢復在切換以前的程序執行位置,每一個線程都須要有本身獨立的程序計數器。


2. 虛擬機棧


虛擬機棧(VM Stack),也有人稱它爲 Java 虛擬機棧、棧或者 JVM 棧,這塊就是你們常常分析一段程序執行時要關注的區域。


JVM 棧是每一個線程私有的,線程建立的同時都會建立對應的 JVM 棧。


JVM 棧中存放的是當前線程中局部基本類型的變量、返回結果以及 Stack Frame,而非基本類型的對象在 JVM 棧上僅存放一個指向堆上的地址。


3. 本地方法棧


本地方法棧(Native Method Stack)與 JVM 棧很類似,本地方法棧也是每一個線程私有的,只不過是服務的對象不一樣,JVM 棧是爲執行 Java 方法服務,而本地方法棧則是爲執行本地方法(Native Method)服務


4. 方法區


方法區(Method Area),也有人稱它爲永久代,是線程共享的區域。


在方法區中,主要存放靜態變量、常量、類信息、運行時常量池以及全部的方法的信息。運行時常量池(Runtime Constant Pool)是方法區的重要一部分,用於存儲編譯器生成的常量和引用。


JDK 1.8 內存模型


如上圖所示,值得注意的是 JDK 1.8 相比 JDK 1.7,JVM 運行時數據區劃分中的方法區(持久代)從 JVM 運行時數據區拿掉了,而在本地內存加入了元數據區(Metadata Memory),簡而言之在 JDK 1.8 中,元數據區替代了方法區(持久代)。


5. 堆


堆(Heap),全部 new 出來的對象實例都存儲在該區域,這塊區域也是線程共享的,也是分析 Java 程序時關注較多的一塊。


6. 其它


直接內存(Direct Memory,有時也稱做堆外內存,是不受 JVM控制的內存。

在 JDK 1.4 中加入了 NIO,引入了一種基於通道(Channel)與緩衝區(Buffer)的 I/O 方式,它可使用 Native 函數庫直接分配堆外內存,而後經過一個存儲在 Java 堆裏面的DirectByteBuffer 對象做爲這塊內存的引用進行操做。

避免了在 Java 堆和 Native 堆中來回複製數據,能在一些場景中顯著提升性能。

4

 寄語寫最後 

本次,主要讓你們瞭解一下 JVM 的內存結構,但願經過本次分享,你們對 JVM 能有個梗概的認識,想要完全掌握還需針對性的彌補,說句內心話,但願可以把這些圖都記在腦子裏,只要作到腦中有圖,心就不慌。

好了,本次就談到這裏,一塊兒聊技術、談業務、噴架構,少走彎路,不踩大坑。會持續輸出原創精彩分享,敬請期待!

推薦閱讀:
程序員進階系列:年少不懂愛家家,懂了已經是猿中人。
程序員進階系列:你真的懂 HelloWorld 嗎?
七夕,帶你生擼一個驗證框架
Java線程池深度揭祕
完全搞懂 Java 線程池,幹啥都再也不發憷
Java程序跑的快,全要靠線程帶
fastjson的這些坑,你誤入了沒?
真實|技術人員該如何站好最後一班崗?
Java 8 的這些特性,你知道嗎?
改掉這些壞習慣,還怕寫不出健壯的代碼?(一)
改掉這些壞習慣,還怕寫不出優雅的代碼? (二)
改掉這些壞習慣,還怕寫不出優雅的代碼? (三)
改掉這些壞習慣,還怕寫不出健壯的代碼? (四)
改掉這些壞習慣,還怕寫不出精簡的代碼? (五)
改掉這些壞習慣,還怕寫不出精簡的代碼? (六)

本文分享自微信公衆號 - 一猿小講(yiyuanxiaojiangV5)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索