JVM異常現象解析
1、Java進程內存不回落
異常現象:針對 Java 應用進程進行壓力測試時,在壓測過程中進程的占用內存逐漸升高,但在壓測結束后,進程占用內存仍然一直很高不回落,奇怪的是此時堆內存占用其實很低。即 Java應用一直占用高內存并且在空閑時也并未將內存歸還給操作系統,這與通常的認知不同。
很多人的認知是:“Java程序在短時間內處理大量任務時會申請使用大量的內存空間,而等任務處理完畢(或者是請求的任務變少時),會有大量的內存被虛擬機回收回來,回收回來的內存由于短期內不再被使用,所以虛擬機會將內存歸還給操作系統。”。但其實是錯的,Java 在進行垃圾回收后,會將不再被使用的對象占用的內存回收,但這些內存如果短期內沒有被接著使用,并不一定會歸還給操作系統,具體會不會歸還是受JVM版本(直接和GC版本有關)、JVM的參數配置影響的。
“JVM 的垃圾回收,只是一個邏輯上的回收,回收的只是 JVM 申請的那一塊邏輯堆區域,將數據標記為空閑之類的操作,并不是調用 free 將內存歸還給操作系統”
要注意,Java 進程內存不回落可能是正常的,此時應該關注的是 JVM 內存使用率有沒有回落。我們平常更應該關注的是 JVM內存的使用率,而不是 Java 進程在整個服務器內占用的內存。JAVA 進程可占用的最大內存就是 -Xmx 的值,JVM 內存使用率達到 100% 且一直不回落的話進程就可能會發生 OOM 即內存溢出。而 JAVA 進程占用內存一直為 -Xmx 的值并不會導致進程發生 OOM。
1.1、操作系統與JVM的內存分配
JVM 的自動內存管理,其實只是先向操作系統申請了一大塊內存,然后自己在這塊已申請的內存區域中進行“自動內存管理”。JAVA 中的對象在創建前,會先從申請的這一大塊內存中劃分出一部分來給這個對象使用,在 GC 時也只是這個對象所處的內存區域數據清空,標記為空閑而已。JVM 歸還內存給操作系統的代價比較大,所以不會輕易進行,即 JVM 不會在每次 GC 后都進行內存的歸還。
雖然代價高,但 JVM 還是提供了這個歸還內存的功能。JVM 提供了-XX:MinHeapFreeRatio和-XX:MaxHeapFreeRatio 兩個參數,用于配置這個歸還策略。
- MinHeapFreeRatio:示例 -XX:MinHeapFreeRatio=20。表示空閑內存最少保留的比例值,當使用內存越來越大,空閑區域小于比值時,會進行擴容,直到達到最大堆內存 Xmx。如果是在收縮過程,剩余內存達到這個閾值后,就會停止收縮。
- MaxHeapFreeRatio:示例 -XX:MaxHeapFreeRatio=70。代表當空閑區域達到該值時,會進行“縮容”,縮容的下限為Xms
不過雖然有這個歸還的功能,不過因為這個代價比較昂貴,所以 JVM 在歸還的時候,是線性遞增歸還的,并不是一次全部歸還。而且經過實測,這個歸還內存的機制,在不同的垃圾回收器,甚至不同的 JDK 版本中還不一樣!
一般為了避免 JVM 頻繁的擴容縮容,我們會將 Xms 和 Xmx 配置為相等的大小,避免這個擴容的操作。
那是不是只要把 Xms 和 Xmx 配置成一樣的大小,這個 JAVA 進程一啟動就會占用這個大小的內存呢?并不是的,不會的,哪怕你 Xms6G,啟動也只會占用實際寫入的內存。這是因為進程在申請內存時,操作系統并不是直接分配物理內存給進程的,而是分配一塊虛擬空間,到真正往這塊虛擬空間寫入數據時才會通過缺頁異常(Page Fault)處理機制分配物理內存。可以簡單地認為操作系統的內存分配是“惰性”的,分配并不會發生實際的占用,有數據寫入時才會發生內存占用。所以哪怕配置了Xms6G,啟動后也不會直接占用 6G 內存,實際占用的內存取決于你有沒有往這 6G 內存區域中寫數據的。
(猜測:當Xms和Xmx設置一樣時,堆內存一開始不會占滿,當占滿時應該不會再鎖絨,因為縮容的下限是 Xms)
正常來說,我們也不需要太關心 JVM 是否有歸還了內存給操作系統,但是如果一臺服務器內有運行著多個 Java 進程,如果都不歸還內存的話就可能導致服務器內存不足,各個進程能使用的內存也不足,這種情況下可能就需要通過配置參數來讓 JVM 能在內存空閑時自動歸還內存。
參考:http://www.rzrgm.cn/hd92/p/16934280.html、https://blog.csdn.net/ancoa/article/details/125354061

浙公網安備 33010602011771號