Java 虛擬機器 JVM 記憶體參數調整設定整理

這陣子都在搞DevSecOps,實作上的一部分重點就是將安全測試導入CI/CD當中。

那SAST(靜態應用程式安全測試)的部分我是使用知名的開源原始碼掃描平台SonarQube,

不過在整合到Jenkins的時候發生了一些小問題,主要應該是 SonarQube 安裝了 Findbugs 插件,

而 Findbugs 由於太過吃記憶體,一值導致產生了記憶體的問題。

不過由於這算是很特別的個案,之後若有機會再針對這個個案寫一篇Debug文章,

這邊就只是先記錄,Debug 的過程當中有需要修改到 JVM 的記憶體參數,

所以需要對參數有些基本的理解,方便未來遇到跟 JVM 相關的問題可以回來看看自己的筆記。

問題

遇到的問題可能會像以下

java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: GC overhead limit exceeded

如果是 java.lang.OutOfMemoryError 的問題

常見的可能是以下幾種情況

(1) 記憶體中載入的資料量過於龐大,如一次從資料庫取出過多資料

(2) 集合類中有對物件的引用,使用完後未清空,使得JVM不能回收

(3) 程式碼中存在死迴圈或迴圈產生過多重複的物件實體

(4) 使用的第三方軟體中的BUG

(5) 啟動引數記憶體值設定的過小

那這邊如果是JVM記憶體值的設定過小,可以嘗試修改參數來調整加大JVM記憶體空間

那有兩大重點

1.知道指令 => 當然要先知道怎麼下指令呀,不然怎麼調整

2.知道參數意思 => 如果你不知道參數意思,亂設定很可能沒辦法解決問題

舉個例子,我當時遇到這個問題,看到網路上有解法寫說輸入以下這串指令

export SONAR_SCANNER_OPTS="-Xmx3062m -XX:MaxPermSize=512m -XX:ReservedCodeCacheSize=128m"

後來就照說下了這個指令,問題當然沒有解決。

而且後來查了一下參數的意思,發現我預設的記憶體根本就比這串指令還要大,

所以下了之後根本也就不會加大我的JVM記憶體最大值。

JVM參數意義

這邊整理一下常見的參數:

-Xms (initial heap size):JVM初始分配的堆記憶體,預設會是實體記憶體的1/64

-Xmx (maximum heap size):JVM最大允許分配的堆記憶體,預設會是實體記憶體的1/4

-XX:PermSize:JVM初始分配的非堆記憶體, 預設會是實體記憶體的1/64

-XX:MaxPermSize: JVM最大允許分配的非堆記憶體, 預設會是實體記憶體的1/4

關於預設記憶體大小可以參考官方文件
https://docs.oracle.com/javase/6/docs/technotes/guides/vm/gc-ergonomics.html
( XX:PermSize 跟 XX:MaxPermSize 的預設值大小我比較不確定,沒看到官方文件。都是聽網路說法)

以上四個參數,其實看可看出,主要就是分成堆記憶體與非推記憶體

堆(Heap)和非堆(Non-heap)記憶體,下面這張圖我覺得可以滿好的幫助理解

接著就來說如何下指令還有一些注意事項

範例一

範例一:

export JAVA_OPTS="-Xms1024m -Xmx1024m -XX:PermSize=512m -XX:MaxPermSize=1024m"

export 就是去修改環境變數,這邊修改的變數是 JAVA_OPTS,

實際上要修改甚麼環境變數要看你遇到的案例,視情況而定,

還有就是稍微注意 export,只再當次登入的情況下有效,如果重新開機就會消失了。

你可以利用下面的指令來稍微確認先前的 JAVA_OPTS 是否已經有設定。

echo $JAVA_OPTS

這邊的指令設定了四個參數 -Xms1024m -Xmx1024m -XX:PermSize=512m -XX:MaxPermSize=1024m

參數意思上面也都提到過了,參數後面帶的就是要調整的大小,

-Xms1024m 表示 初始分配的堆記憶體為 1024 MB的記憶體
-Xmx1024m 表示 最大允許分配的堆記憶體為 1024 MB的記憶體
-XX:PermSize=512m 表示 初始分配的非堆記憶體為 512 MB的記憶體
-XX:MaxPermSize=1024m 表示 最大允許分配的非堆記憶體為 1024 MB的記憶體

範例二

範例二:

export SONAR_SCANNER_OPTS="-Xms32g -Xmx32g -XX:PermSize=1g -XX:MaxPermSize=16g -XX:ReservedCodeCacheSize=512m"

範例二可以注意到這邊修改的環境變數就不是 JAVA_OPTS,而是 SONAR_SCANNER_OPTS,是由於我的需要所設定。

再來是可以注意到參數調整容量的部分不是用m而是改成g了,可以直接換單位來指定GB

-Xms32g 表示 初始分配的堆記憶體為 32 GB的記憶體
-Xmx32g 表示 最大允許分配的堆記憶體為 32 GB的記憶體
-XX:PermSize=1g 表示 初始分配的非堆記憶體為 1 GB的記憶體
-XX:MaxPermSize=16g 表示 最大允許分配的非堆記憶體為 16 GB的記憶體
-XX:ReservedCodeCacheSize=512m 表示 保留的程式碼快取空間為 512 MB

注意事項

然後有幾點注意事項:

1.-Xms 的值不可大於 -Xmx,還有 -XX:PermSize 的值不可大於 -XX:MaxPermSize

2.-Xmx 和 -XX:MaxPermSize 的總和不可超過實體記憶體的最大值

3.一般建議 -Xms 與 -Xmx 的值設定為相同 (官方也是這樣建議)

Oracle recommends that -Xmn and -Xmx be set to the same value. This eliminates potentially costly heap reallocations, and can reduce the amount of heap fragmentation that can occur

補充

此篇筆記,之後若有知識增長或是觀念修正,都會滾動式更新。

範例中的指令單純是參考,並不建議在不明白意義的情況下複製貼上到自己的機器中。

若有大神要糾正或是覺得可以補充的也歡迎留言、寄信、私訊給我!(在臉書粉專私訊會比較快回)

參考來源

https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/313033/

https://blog.jsjs.org/?p=1230

https://www.gushiciku.cn/pl/poUC/zh-tw

https://www.itread01.com/p/284188.html

https://ithelp.ithome.com.tw/articles/10211000

發佈留言