淺談JVM整體架構與調優引數

华为云开发者联盟發表於2024-04-02
本文分享自華為雲社群《【效能最佳化】JVM整體架構與調優引數說明》,作者: 冰 河。

JVM的分類

這裡,我們先來說說什麼是VM吧,VM的中文含義為:虛擬機器,指的是使用軟體的方式模擬具有完整硬體系統功能、執行在一個完全隔離環境中的完整計算機系統,是物理機的軟體實現。

常用的虛擬機器有:VMWare、Virtual Box,Java Virtual Machine(JVM,Java虛擬機器)。

這裡,我們重點聊的就是JVM,Java虛擬機器。看下圖。

825b2eec70954776a8fe95791340256c.png

這張圖看起來還是比較簡單的,JVM執行於作業系統之上,作業系統是執行在計算機硬體上的。

關於JVM,其實有很多大廠開發了不同版本的JVM,比較知名的有:Sun HotSpot VM、BEA JRockit VM、IBM J9 VM、 Azul VM、 Apache Harmony、 Google Dalvik VM、 Microsoft JVM等等。

現在使用的比較多的JDK8版本就是Sun HotSpot VM與BEA JRockit VM合併之後開發出的JDK版本。

JVM的構成

JVM主要由三個子系統構成,分別為:類載入器子系統、執行時資料區(記憶體結構)和位元組碼執行引擎。

fa4b9d489ffb407ba6e9ffaf9b4ed420.png

為了更好的理解JVM,我們來看一下JVM的全貌圖。

06afe983572e4c529d6b49c5b57f055f.png

當我們開發Java程式時,首先會編寫.java檔案,之後,會將.java檔案編譯成.class檔案。

JVM中,會透過類裝載子系統將.class檔案的內容裝載到JVM的執行時資料區,而JVM的執行時資料區又會分為:方法區、堆、棧、本地方法棧和程式計數器 幾個部分。

在裝載class檔案的內容時,會將class檔案的內容拆分為幾個部分,分別裝載到JVM執行時資料區的幾個部分。其中,值得注意的是:程式計數器的作用是:記錄程式執行的下一條指令的地址。

方法區也叫作元空間,主要包含了:執行時常量池、型別資訊、欄位資訊、方法資訊、類載入器的引用、對應的Class例項的引用等資訊。

在JVM中,程式的執行是透過執行引擎進行的,執行引擎會呼叫本地方法的介面來執行本地方法庫,進而完成整個程式邏輯的執行。

我們常說的垃圾收集器是包含在執行引擎中的,在程式的執行過程中,執行引擎會開啟垃圾收集器,並在後臺執行,垃圾收集器會不斷監控程式執行過程中產生的記憶體垃圾資訊,並根據相應的策略對垃圾資訊進行清理。

這裡,大家需要注意的是:棧、本地方法棧和程式計數器是每個執行緒執行時獨佔的,而方法區和堆是所有執行緒共享的。所以,棧、本地方法棧和程式計數器不會涉及執行緒安全問題,而方法區和堆會涉及執行緒安全問題。

方法區(元空間)

很多小夥伴一看到方法區三個字,腦海中的第一印象可能是儲存方法的地方吧。

實則不然,方法區的另一個名字叫作元空間,相信不少小夥伴或多或少的聽說過元空間。這個區域是JDK1.8中劃分出來的。主要包含:執行時常量池、型別資訊、欄位資訊、方法資訊、類載入器的引用、對應的Class例項的引用等資訊。方法區中的資訊能夠被多個執行緒共享。

例如,在程式中宣告的常量、靜態變數和有關於類的資訊等的引用,都會存放在方法區,而這些引用所指向的具體物件 一般都會在堆中開闢單獨的空間進行儲存,也可能會在直接記憶體中進行儲存。

622d2f8cbaf5494a8f00c10756eeeb4a.png

堆中主要儲存的是實際建立的物件,也就是會儲存透過new關鍵字建立的物件,堆中的物件能夠被多個執行緒共享。堆中的資料不需要事先明確生存期,可以動態的分配記憶體,不再使用的資料和物件由JVM中的GC機制自動回收。對JVM的效能調優一般就是對堆記憶體的調優。

Java中基本型別的包裝類:Byte、Short、Integer、Long、Float、Double、Boolean、Character型別的資料是儲存在堆中的。

堆一般會被分成年輕代和老年代。而年輕代又會被進一步分為1個Eden區和2個Survivor區。在記憶體分配上,如果保持預設配置的話,年輕代和老年代的記憶體大小比例為1 : 2,年輕代中的1個Eden區和2個Survivor區的記憶體大小比例為:8 : 1 : 1。

3412dfc8c91049e2a0837b78e0cf5db2.png

棧一般又叫作執行緒棧或虛擬機器棧,一般儲存的是區域性變數。在Java中,每個執行緒都會有一個單獨的棧區,每個棧中的元素都是私有的,不會被其他的棧所訪問。棧中的資料大小和生存期都是確定的,存取速度比較快。

在Java中,所有的基本資料型別(byte、short、int、long、float、double、boolean、char)和引用變數(物件引用)都是在棧中的。一般情況下,執行緒退出或者方法退出時,棧中的資料會被自動清除。

程式在執行過程中,會在棧中為不同的方法建立不同的棧幀,在棧幀中又包含了:區域性變數表、運算元棧、動態連結和方法出口。

e2e7eae6e0d5405ca6647c6ca5777162.png

關於區域性變數表、運算元棧、動態連結和方法出口的具體作用,會在《架構師進階系列》中的後續文章中詳細闡述。

棧中一般會儲存物件的引用,這些引用所指向的具體物件一般都會在堆中開闢單獨的地址空間進行儲存,也有可能儲存在直接記憶體中。

3fadd1a139fd40978e76284614d84575.png

注意: 這裡說的是這些引用所指向的具體物件一般都會在堆中開闢單獨的地址空間進行儲存,也有可能儲存在直接記憶體中。

因為在JVM中,如果開啟了逃逸分析和標量替換,則可能不會再在堆上建立物件,可能會將物件直接分配到棧上,也可能不再建立物件,而是進一步分解物件中的成員變數,將其直接在棧上分配空間並賦值。

本地方法棧

本地方法棧相對來說比較簡單,就是儲存native方法進入區域的地址。

例如,在Java中建立執行緒,呼叫Thread物件的start()方法時,會透過本地方法start0()呼叫作業系統建立執行緒的方法。此時,本地方法棧就會儲存start0()方法進入區域的記憶體地址。

程式計數器

程式計數器也叫作PC計數器,只要儲存的是下一條將要執行的命令的地址。

JVM調優引數

在JVM中,主要是對堆(新生代)、方法區和棧進行效能調優。各個區域的調優引數如下所示。

  • 堆:-Xms、-Xmx
  • 新生代:-Xmn
  • 方法區(元空間):-XX:MetaspaceSize、-XX:MaxMetaspaceSize
  • 棧(執行緒):-Xss

為了更加直觀的表述,我們可以將JVM的記憶體區域和對應的調優引數總結成下圖所示。

d2e8e09e945d434da6dfc9de1a6043b1.png

在設定JVM啟動引數時,需要特別注意方法區(元空間)的引數設定。

關於方法區(元空間)的JVM引數主要有兩個:-XX:MetaspaceSize和-XX:MaxMetaspaceSize。

-XX:MetaspaceSize: 指的是方法區(元空間)觸發Full GC的初始記憶體大小(方法區沒有固定的初始記憶體大小),以位元組為單位,預設為21M。達到設定的值時,會觸發Full GC,同時垃圾收集器會對這個值進行修改。

如果在發生Full GC時,回收了大量記憶體空間,則垃圾收集器會適當降低此值的大小;如果在發生Full GC時,釋放的空間比較少,則在不超過設定的-XX:MetaspaceSize值或者在沒設定-XX:MetaspaceSize的值時不超過21M,適當提高此值。

-XX:MaxMetaspaceSize: 指的是方法區(元空間)的最大值,預設值為-1,不受堆記憶體大小限制,此時,只會受限於本地記憶體大小。

最後需要注意的是: 調整方法區(元空間)的大小會發生Full GC,這種操作的代價是非常昂貴的。如果發現應用在啟動的時候發生了Full GC,則很有可能是方法區(元空間)的大小被動態調整了。

所以,為了儘量不讓JVM動態調整方法區(元空間)的大小造成頻繁的Full GC,一般將-XX:MetaspaceSize和-XX:MaxMetaspaceSize設定成一樣的值。例如,實體記憶體8G,可以將這兩個值設定為256M

最後,我們一起看下在實體記憶體8G的情況下,啟動應用程式時,可以設定的JVM引數。當然,我這裡給出的是一些經驗值,實際部署到生產環境時,需要經過壓測找到最佳的引數值。

  • 啟動SpringBoot
java ‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐jar xxx.jar
  • 啟動Tomcat(Linux)

在Tomcat bin目錄下catalina.sh檔案裡配置。

‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M
  • 啟動Tomcat(Windows)

在Tomcat bin目錄下catalina.bat檔案裡配置。

‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M

總結

今天,我們一起學習了JVM的整體架構和調優引數,主要包括:JVM的總體結構、JVM的分類、JVM的構成和調優引數。你學會了嗎?歡迎在文末留言說出你的想法,如果你有更好的見解,也可以在文末留言和大家交流。

點選關注,第一時間瞭解華為雲新鮮技術~

相關文章