關於虛擬機內存和JVM內存設置的思考

關於虛擬機內存和JVM內存設置的思考


背景

最近有同事總問JVM的設置問題.
之前總結過不少. 但是感覺沒法講對方說服
當然了, 自己能力有限, 只能自說自話. 
現在這個就是留存一個底稿. 希望能人能幫忙解釋

關於內存和CPU的觀點

CPU的能力有上限. 一般情況下不建議讓CPU處於高峯作業. 
尤其是 x86 的機器 有超線程, 其實CPU顯示使用率超過一半的CPU時就已經快到上限了.

所以機器的性能 40%左右的CPU 可能相應時間和吞吐量都比較好一些.
超過 50% 超線程中的 另外一個線程開始爭搶寄存器, 性能就會下降. 

但是CPU使用率有非常依賴內存的使用情況.
內存高, 不需要swap,不需要gc, 不需要線程切換換入換出. CPU的壓力就小
會主要進行 業務操作的CPU. 但是如果機器內存不專一, 會導致 sys cpu升高
如果JVM設置的不合理, 經常fullgc 會倒是 user cpu升高.

CPU性能好的機器 可以容忍, 但是如果CPU性能不好,尤其是訪存和內存帶寬不高的機器.
此時就會導致非常嚴重的性能問題.

所以提升性能的本質, 就是減少內存的非主觀操作, 減少FullGC,減少內存被動的換入換出.

關於信創的思考

現在國家隊信創的要求主要在於CPU
磁盤和內存可以選用非國產的設備.

因爲國產化是一個持久化的過程,現階段來看,
國產CPU的性能距離Intel最新的CPU還有5-8年的差距.

此時揚長避短就是一個很必要的措施. 
增加內存, 減少CPU的工作, 避免swap,換入換出,fullGC
通過減少CPU的壓力來提高響應效率和增加吞吐量.

高速的CPU,高速的內存能夠減少內存latch,lock以及數據庫的lock
的持有時間, 能夠減少lock wait, 減少死鎖出現的概率
提高產品執行效率, 降低響應時間. 提高客戶體驗. 

國產化的虛擬機系統可能會增加一些安全審計相關的組件. 會都佔用部分內存
虛擬機的內存要至少大於 jvm的堆區 10G 左右才比較安全. 

因爲根據之前的經驗. 系統會佔用 1-2G的內存.
非堆區佔用 4G 左右的內存
因爲讀寫文件會佔用buffer和cache, 也會佔用2G左右的內存. 
還要空餘一部分內存應對突發的響應. 

如果內存不足,導致使用到了swap, 性能會指數級的下降.
所以最開始的K8S 都要求關閉swap, 出現內存不夠, 立即OOM
通過fast failure 的方式來避免 應用卡頓, 產生不可控的事件.

一個現象

公司裏面一堆機器在跑自動化.
其中有一臺機器的FullGC特別高
發現 這個機器的 xmx的設置不合理,跟其他機器不一樣.

稍微大一點的內存能夠極大的減少fullGC的次數. 

內存配置與運行情況

宿主機的監控情況爲: 
PID  USER   VIRT    RES    SHR   S  %CPU  %MEM    TIME+   COMMAND
8422 root   19.5g  14.1g 144680  S  771.5  29.8   2843:18 java

jvm的內存配置爲: 10G
-XX:InitialHeapSize=10737418240 -XX:MaxHeapSize=10737418240

然後發現jvm在瘋狂GC

圖示

image


說明

圖中很明顯可以看到 除了這一個之外, 其他額FullGC次數非常少.

這麼多fullGC 跟內存大小不太足有很大的關係. 

dump分析

time jmap -dump:live,format=b,file=/20240412.hprof  8422

內存使用情況

image


內存使用分析

最多的幾個內存區域
org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
org.springframework.data.jpa.mapping.JpaMetamodelMappingContext
org.hibernate.metamodel.internal.MetamodelImpl
org.hibernate.service.internal.SessionFactoryServiceRegistryImpl

這些基本上佔用了接近2G的內存空間. 

理論上這些應該都是必須的
剩下 5G的空間是業務代碼邏輯使用
所以就太小了. 

image


關於內存佔用的分析

堆區設置 10G
top顯示內存 14.1G

多與的 4.1G 是非堆區. 

比較關鍵的數據應該是包含:
1. 線程棧區
2. 元數據區(包含方法區,也就是類的元數據)
3. 直接內存
4. 本地內存
5. gc,jit等c語言編寫的工具使用的內存區域.

一般情況下 top 使用的內存減去 xms=xmx=堆區 內存的情況
就應該是比較準確的非堆區整體用量. 

內存不足時CPU使用增加

整個java使用的時間爲:
2-05:11:50
合計爲:  3200分鐘左右

計算如下的 FullGC的總時間爲: 2000分鐘 
換句話說, FullGC/ALL 時間的比率爲: 63% 
所以機器其實一直在瘋狂的出工, 但是出的力就比較少. 
  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 8453 root      20   0   19.1g  14.0g 144712 S  0.0 29.7 253:24.53 java
 8454 root      20   0   19.1g  14.0g 144712 S  0.0 29.7 253:11.33 java
 8451 root      20   0   19.1g  14.0g 144712 S  0.0 29.7 253:05.68 java
 8455 root      20   0   19.1g  14.0g 144712 S  0.0 29.7 253:01.91 java
 8452 root      20   0   19.1g  14.0g 144712 S  0.0 29.7 252:55.15 java
 8449 root      20   0   19.1g  14.0g 144712 S  0.0 29.7 252:54.04 java
 8450 root      20   0   19.1g  14.0g 144712 S  0.0 29.7 252:53.27 java
 8448 root      20   0   19.1g  14.0g 144712 S  0.0 29.7 252:52.40 java

相同的一個設置爲 24G堆區的機器

CPU總時間爲:
root     23429 23412 99 4月11 ?       1-05:09:28
1750 分鐘左右.

查看fullGC相關的時間  累計才 20分鐘.  這樣的話 大部分業務時間就對應的上了.
23450 root      20   0   35.5g  30.1g  24332 S   0.0  63.8   2:18.84 java
23451 root      20   0   35.5g  30.1g  24332 S   0.0  63.8   2:18.81 java
23452 root      20   0   35.5g  30.1g  24332 S   0.0  63.8   2:18.97 java
23453 root      20   0   35.5g  30.1g  24332 S   0.0  63.8   2:19.43 java
23454 root      20   0   35.5g  30.1g  24332 S   0.0  63.8   2:17.78 java
23455 root      20   0   35.5g  30.1g  24332 S   0.0  63.8   2:18.71 java
23456 root      20   0   35.5g  30.1g  24332 S   0.0  63.8   2:19.03 java
23457 root      20   0   35.5g  30.1g  24332 S   0.0  63.8   2:18.52 java

結論

相同的CPU在不同內存配置的情況下使用的CPU時間有一倍的差距
其中差異的大部分都被因爲堆區不太夠導致的fullGC消耗的時間來產生.
所以增加堆區, 到一小時不超過一次FullGC的場景下.
機器的性能會比較好一些. 

業務高峯期應該建議不要有fullGC的不然會導致卡頓. 
如果業務量比較大的FullGC可能至少會產生 10秒-30秒的等待
會導致客戶體驗明顯下降. 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章