JVM異常現象解析

wenxuehai發表於2024-10-12

1、Java程序記憶體不回落

異常現象:針對 Java 應用程序進行壓力測試,在壓測過程中程序記憶體逐漸升高,但在壓測結束後,程序佔用記憶體仍然一直很高不回落,奇怪的是此時堆記憶體佔用其實很低。即 Java應用一直佔用高記憶體並且在空閒時也並未將記憶體歸還給作業系統,這與通常的認知不同。

“JVM 的垃圾回收,只是一個邏輯上的回收,回收的只是 JVM 申請的那一塊邏輯堆區域,將資料標記為空閒之類的操作,不是呼叫 free 將記憶體歸還給作業系統”

1.1、作業系統與JVM的記憶體分配

JVM 的自動記憶體管理,其實只是先向作業系統申請了一大塊記憶體,然後自己在這塊已申請的記憶體區域中進行“自動記憶體管理”。JAVA 中的物件在建立前,會先從申請的這一大塊記憶體中劃分出一部分來給這個物件使用,在 GC 時也只是這個物件所處的記憶體區域資料清空,標記為空閒而已。JVM 歸還記憶體給作業系統的代價比較大,所以不會輕易進行,JVM 不會在每次 GC 後都進行記憶體的歸還。

雖然代價高,但 JVM 還是提供了這個歸還記憶體的功能。JVM 提供了-XX:MinHeapFreeRatio和-XX:MaxHeapFreeRatio 兩個引數,用於配置這個歸還策略。

  • MinHeapFreeRatio:表示空閒記憶體最少保留的比例值,當使用記憶體越來越大,空閒區域小於比值時,會進行擴容,擴容的上限為 Xmx。如果是在收縮過程,剩餘記憶體達到這個閾值後,就會停止收縮。
  • MaxHeapFreeRatio:代表當空閒區域達到該值時,會進行“縮容”,縮容的下限為Xms

不過雖然有這個歸還的功能,不過因為這個代價比較昂貴,所以 JVM 在歸還的時候,是線性遞增歸還的,並不是一次全部歸還。而且經過實測,這個歸還記憶體的機制,在不同的垃圾回收器,甚至不同的 JDK 版本中還不一樣!

一般為了避免 JVM 頻繁的擴容縮容,我們會將 Xms 和 Xmx 配置為相等的大小,避免這個擴容的操作。

那是不是隻要把 Xms 和 Xmx 配置成一樣的大小,這個 JAVA 程序一啟動就會佔用這個大小的記憶體呢?並不是的,不會的,哪怕你 Xms6G,啟動也只會佔用實際寫入的記憶體。這是因為程序在申請記憶體時,作業系統並不是直接分配實體記憶體給程序的,而是分配一塊虛擬空間,到真正往這塊虛擬空間寫入資料時才會透過缺頁異常(Page Fault)處理機制分配實體記憶體。可以簡單地認為作業系統的記憶體分配是“惰性”的,分配並不會發生實際的佔用,有資料寫入時才會發生記憶體佔用。所以哪怕配置了Xms6G,啟動後也不會直接佔用 6G 記憶體,實際佔用的記憶體取決於你有沒有往這 6G 記憶體區域中寫資料的。

相關文章