Java(JVM)內存模型
理解JVM內存模型
Java內存管理是理解Java垃圾回收工作原理的重要基礎。今天,我們將探討Java中的內存管理、JVM內存的不同部分,以及如何監控和調優垃圾回收。
Java (JVM) 內存模型
如上圖所示,JVM內存分為多個部分。從廣義上講,JVM堆內存被物理地分為兩部分:年輕代(Young Generation)和老年代(Old Generation)。
Java中的內存管理 - 年輕代
年輕代是創建所有新對象的地方。當年輕代填滿時,會進行垃圾回收,這種回收稱為小垃圾回收(Minor GC)。年輕代分為三個部分:伊甸園內存(Eden Memory)和兩個幸存者內存空間(Survivor Memory)。關于年輕代空間的重要點:
- 大多數新創建的對象位于伊甸園內存空間。
- 當伊甸園空間填滿對象時,進行Minor GC,所有幸存對象被移動到其中一個幸存者空間。
- Minor GC還檢查幸存對象并將其移動到另一個幸存者空間。因此在任何時候,某個幸存者空間始終為空。
- 經過多次GC循環幸存的對象將移動到老年代內存空間。通常,通過設置年輕代對象的年齡閾值來決定它們何時可以提升到老年代。
Java中的內存管理 - 老年代
老年代內存包含長壽命對象,這些對象在多輪Minor GC后仍然存活。通常,當老年代內存滿時,會進行垃圾回收,稱為大垃圾回收(Major GC),通常需要更長時間。
停止世界事件
所有的垃圾回收都是“停止世界”事件,因為所有應用程序線程會在操作完成之前被停止。由于年輕代保留短期對象,Minor GC非常快,應用程序不會受到影響。然而,Major GC需要較長時間,因為它會檢查所有活躍對象。應盡量減少Major GC,因為這會使應用程序在垃圾回收期間無響應。如果您的應用程序需要響應性而發生大量Major GC,您會注意到超時錯誤。垃圾收集器所需的時間取決于所使用的垃圾回收策略。因此,監控和調優垃圾收集器以避免高響應性應用中的超時是必要的。
Java內存模型 - 永久代
永久代(Permanent Generation或Perm Gen)包含JVM所需的應用程序元數據,以描述應用程序中使用的類和方法。請注意,Perm Gen并不是Java堆內存的一部分。Perm Gen由JVM在運行時根據應用程序使用的類填充,還包含Java SE庫的類和方法。Perm Gen對象在完全垃圾回收中被收集。
Java內存模型 - 方法區
方法區是Perm Gen的一部分,用于存儲類結構(運行時常量和靜態變量)及方法和構造函數的代碼。
Java內存模型 - 內存池
內存池是由JVM內存管理器創建的,用于創建不可變對象的池(如果實現支持)。字符串池是這種內存池的一個好例子。內存池可以屬于堆或Perm Gen,這取決于JVM內存管理器的實現。
Java內存模型 - 運行時常量池
運行時常量池是每個類的運行時常量池的表示,包含類運行時常量和靜態方法。運行時常量池是方法區的一部分。
Java內存模型 - Java棧內存
Java棧內存用于線程的執行,包含方法特定的短期值和對堆中其他對象的引用。您應該了解堆內存與棧內存之間的區別。
Java中的內存管理 - Java堆內存開關
Java提供了許多內存開關,我們可以用它們來設置內存大小及其比例。常用的內存開關包括:
VM開關 | VM開關描述 |
---|---|
-Xms | 設置JVM啟動時的初始堆大小 |
-Xmx | 設置最大堆大小 |
-Xmn | 設置年輕代的大小,其余空間用于老年代 |
-XX:PermGen | 設置永久代內存的初始大小 |
-XX:MaxPermGen | 設置Perm Gen的最大大小 |
-XX:SurvivorRatio | 提供伊甸園空間與幸存者空間的比例,例如,如果年輕代大小為10m且VM開關為-XX:SurvivorRatio=2,則將保留5m用于伊甸園空間,每個幸存者空間各保留2.5m。默認值為8。 |
-XX:NewRatio | 提供老代與新生代大小的比例。默認值為2。 |
大多數時候,上述選項是足夠的,但如果您想查看其他選項,請查看JVM選項官方頁面。
Java中的內存管理 - Java垃圾回收
Java垃圾回收是識別并刪除內存中未使用對象的過程,以釋放空間供未來處理創建的對象。Java編程語言的一個最佳特性是自動垃圾回收,而不像其他編程語言(如C)那樣需要手動管理內存分配和釋放。垃圾收集器是在后臺運行的程序,檢查內存中的所有對象,找出未被程序任何部分引用的對象。所有這些未引用的對象都會被刪除,并回收空間以分配給其他對象。垃圾回收的基本步驟包括:
- 標記:這是垃圾收集器識別哪些對象在使用、哪些對象未使用的第一步。
- 正常刪除:垃圾收集器移除未使用的對象,回收空間以分配給其他對象。
- 壓縮刪除:為了提高性能,在刪除未使用對象后,可以將所有幸存對象一起移動,這將提高新對象的內存分配性能。
簡單的標記和刪除方法有兩個問題:
- 第一,它效率不高,因為大多數新創建的對象很快會變為未使用。
- 第二,經過多次垃圾回收周期的對象很可能在未來的周期中仍然在使用。
上述簡單方法的缺點是Java垃圾回收是代際的,因此我們在堆內存中有年輕代和老年代空間。我已經在上面解釋了如何根據Minor GC和Major GC掃描和移動對象。
Java中的內存管理 - Java垃圾回收類型
我們可以在應用程序中使用五種垃圾回收類型。只需使用JVM開關啟用應用程序的垃圾回收策略。讓我們逐一看一下這些類型。
- 串行GC (-XX:+UseSerialGC):使用簡單的標記-清除-壓縮方法進行年輕代和老年代垃圾回收,即Minor和Major GC。串行GC適用于客戶端機器,如簡單的獨立應用程序和小型CPU的機器。適用于內存占用小的應用程序。
- 并行GC (-XX:+UseParallelGC):與串行GC相同,但它為年輕代垃圾回收生成N個線程,其中N是系統中的CPU核心數量。我們可以使用-XX:ParallelGCThreads=nJVM選項設置n。對于大型應用程序(特別是高吞吐量應用程序),該方法比串行更有效,因為在小于CPU核心的機器上,它不會產生額外的開銷。
- 并發標記清除GC (-XX:+UseConcMarkSweepGC):具有最小的應用程序停機時間。該GC使用多個線程進行Minor GC,并使用兩個階段并發標記清除對象,幾乎沒有暫停。與并行GC相比,這種方式在高響應性應用程序中表現良好。
- G1 GC (-XX:+UseG1GC):通過將堆劃分為多個區域來工作,它將小部分空間分配給年輕代和老年代。它的垃圾回收過程分為多個階段,通常表現良好。G1 GC在大規模應用程序中最有效,尤其是在大堆內存和大響應時間敏感應用程序中。我們還可以通過設置-XX:MaxGCPauseMillis=200來定義G1 GC的最大暫停時間。
- ZGC (-XX:+UseZGC):適用于內存很大的應用程序,基本上旨在支持大規模應用程序。它在高響應性應用程序中表現良好,適合在處理大量內存時有超低停機時間。
以上是內存管理中的基本概念。下面我們將探討如何監控和調優Java垃圾回收器,以便您可以優化應用程序性能并避免出現停機時間。
監控和調優垃圾回收
為了優化Java應用程序中的內存管理,您可以使用以下幾種方法監控和調優垃圾回收。
- 使用JVisualVM或其他監控工具以獲取內存使用情況的詳細視圖。
- 使用JConsole進行實時監控。
- 收集垃圾回收日志以獲取數據并分析堆內存使用情況。
- 優化堆大小和年輕代和老年代的比例,以確保內存管理高效。
- 減少創建短期對象以降低垃圾回收頻率。
監控與調優是確保您的Java應用程序運行良好的關鍵部分。通過仔細分析和優化,您可以提升應用程序的性能。
綜上所述,深入理解Java內存模型是提升Java應用性能的重要步驟。通過有效管理內存和優化垃圾回收,您將能夠提高應用的響應性和穩定性。
若你想提升Java技能,可關注我們的Java培訓課程。