爲什麼JVM佔用了超過-Xmx配置的內存?

爲什麼JVM佔用了超過-Xmx配置的內存?

2022-06-05893閱讀5分鐘
 

持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第9天,點擊查看活動詳情

前言

一般JVM我們都會配置上 Xmx參數,限制堆內存的使用

但是在top指令或者其他的指令上看到的值卻不是我們期望的Xmx的數字

實際上Xmx佔用的內存只是JVM所佔內存的一部分,JVM經過各種版本的演化,實際上有了許多的變化

可以從下圖看出

image.png

一個比較大的變化是 JDK8 已經沒有了PermSpace, 增加了 metaspace 的區域,用來保存常量池和類常量池,這部分的內存不是分配在堆上的,而是分配在堆外的,通常如果不設置的話,這部分會無限增長,使用 jmap -heap 指令看出maxmetaspacesize的值非常大,可以認爲就是沒有限制。

這部分的堆外內存只有在full gc時纔會被回收

image.png

metaspace

可以通過如下幾個參數對metaspace 的大小進行限制

  • -XX:MetaspaceSize=N :這個參數是初始化的Metaspace大小,該值越大觸發Metaspace GC的時機就越晚。隨着GC的到來,虛擬機會根據實際情況調控Metaspace的大小,可能增加上線也可能降低。在默認情況下,這個值大小根據不同的平臺在12M到20M浮動。使用java -XX:+PrintFlagsInitial命令查看本機的初始化參數,-XX:Metaspacesize爲21810376B(大約20.8M)
  • -XX:MaxMetaspaceSize=N :這個參數用於限制Metaspace增長的上限,防止因爲某些情況導致Metaspace無限的使用本地內存,影響到其他程序。默認無上限。
  • -XX:MinMetaspaceFreeRatio=N :當進行過Metaspace GC之後,會計算當前Metaspace的空閒空間比,如果空閒比小於這個參數,那麼虛擬機將增長Metaspace的大小。在本機該參數的默認值爲40,也就是40%。設置該參數可以控制Metaspace的增長的速度,太小的值會導致Metaspace增長的緩慢,Metaspace的使用逐漸趨於飽和,可能會影響之後類的加載。而太大的值會導致Metaspace增長的過快,浪費內存。
  • -XX:MaxMetasaceFreeRatio=N :當進行過Metaspace GC之後, 會計算當前Metaspace的空閒空間比,如果空閒比大於這個參數,那麼虛擬機會釋放Metaspace的部分空間。在本機該參數的默認值爲70,也就是70%。
  • -XX:MaxMetaspaceExpansion=N :Metaspace增長時的最大幅度。在本機上該參數的默認值爲5452592B(大約爲5MB)
  • -XX:MinMetaspaceExpansion=N :Metaspace增長時的最小幅度。在本機上該參數的默認值爲340784B(大約330KB爲)

一個誤解

使用jstat -gcutil的結果中的M代表的是元空間的使用比例。又例如:測試環境上某個服務爲例,配置了-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m,通過jstat -gcutil pid查看M的值爲98.32,即Meta區使用率也達到了98.32%
然後,再通過jstat -gc 4210 2s 3命名查看,結果如下圖所示,計算MU/MC即Meta區的使用率確實達到了98.32%,但是MC,即Metaspace Capacity只有55296k,並不是參數MetaspaceSize指定的256m:

image.png

那麼-XX:MetaspaceSize=256m的含義到底是什麼呢?

其實,這個JVM參數是指Metaspace擴容時觸發FullGC的初始化閾值,也是最小的閾值。這裏有幾個要點需要明確:

  • 如果沒有配置-XX:MetaspaceSize,那麼觸發FGC的閾值是21807104(約20.8m),可以通過jinfo -flag MetaspaceSize pid得到這個值;jps -v也可以查看jvm的參數設置情況。
  • 如果配置了-XX:MetaspaceSize,那麼觸發FGC的閾值就是配置的值;
  • Metaspace由於使用不斷擴容到-XX:MetaspaceSize參數指定的量,就會發生FGC;且之後每次Metaspace擴容都可能會發生FGC(至於什麼時候會,比較複雜,跟幾個參數有關);
  • 如果Old區配置CMS垃圾回收,那麼擴容引起的FGC也會使用CMS算法進行回收;
  • 如果MaxMetaspaceSize設置太小,可能會導致頻繁FullGC,甚至OOM;

建議

  • MetaspaceSizeMaxMetaspaceSize設置一樣大;
  • 具體設置多大,建議穩定運行一段時間後通過jstat -gc pid確認且這個值大一些,對於大部分項目256m即可。目前我們的使用也是這一個值

\

實際的JVM內存佔用如何計算

Max memory = [-Xmx] + [-XX:MaxPermSize] + number_of_threads * [-Xss]

所以實際如果我們配置瞭如下參數

-Xmx512m -Xss1m -MaxMetaSpaceSize=256m 的JVM實際佔用的大小爲  512+(1*線程數)+256

可以通過如下指令輸出JVM 的內存佔用,使用這個指令需要在JVM啓動時添加參數 

java -XX:NativeMemoryTracking=detail

jcmd pid VM.native_memory

輸出如下,可以看到整個JVM使用的內存就是由一下的這些部分組成的

image.png

總結

當時的服務實際上時佔用了2.5G的內存,這部分因爲當時沒有加上跟蹤參數,所以暫時還無法查看到

根據以上信息推論可以推論得到,佔用的大頭應該是metaspace

這部分的內存一開始並沒有限制,不會因爲這部分的內容觸發 full gc,動態產生的class都會放在這個區域,進程運行的時間越長,這個區域就會越大,同時也會觀察到實際的堆佔用並沒有增加

現在開發環境採用的參數爲  -Xmx512m -Xss512k -XX:MaxMetaspaceSize=256m,需要觀察一段時間看是否限制住了

也可以通過命令 jcmd pid VM.native_memory 建立的快照來對比發生泄漏的區域,找到問題的根源

 

轉自 爲什麼JVM佔用了超過Xmx配置的內存? - 掘金 (juejin.cn)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章