Docker環境下Spring Boot應用記憶體飆升分析與解決方案

相關標籤:1.docker login、docker push、docker pull 常見報錯 FAQ
2.如何在 Ubuntu 20 上安裝 Docker

Spring Boot 簡介

一個簡易 Spring Boot 應用幾乎沒有使用者訪問,記憶體卻高達1.2G,非常驚人。

Spring Boot 應用記憶體飆升


一個簡易 Spring Boot 應用幾乎沒有使用者訪問,記憶體卻高達1.2G,非常驚人。
服務狀態

由於服務數量少、伺服器資源充裕,很多服務啟動時未設定 JVM 參數(遺留問題)。結果是每個服務啟動就佔用1.2G~2G 記憶體,而有些服務根本用不了這麼多。那麼,未設定 JVM 記憶體參數時,Spring Boot 的 JVM 預設記憶體配置是怎樣的?

Spring Boot JVM 預設記憶體配置


運行 Spring Boot 專案時,如果未設定 JVM 記憶體參數,Spring Boot 預設使用 JVM 自身的配置策略。資源充裕時,開發人員通常不太關注記憶體設定。但一旦遇到資源緊張或需要 JVM 最佳化,就需要瞭解預設記憶體配置策略。
JVM 記憶體最常見的設定是初始堆記憶體(-Xms)和最大堆記憶體(-Xmx)。很多人懶得設定,直接使用 JVM 預設值。尤其在開發環境中,如果啟動的微服務較多,記憶體很快就會被耗盡。

Spring Boot JVM 預設記憶體配置策略分為兩種情境:大記憶體空間情境和小記憶體空間情境(實體記憶體不足192M)。
以4GB 記憶體為例,初始堆記憶體和最大堆記憶體如下:

Spring Boot 預設最大堆記憶體占實體記憶體的 1/4,應用超過此限制將拋出 OutOfMemoryError 異常。初始堆記憶體為實體記憶體的 1/64。
如果應用運行在手機或實體記憶體不足192M 的環境中,JVM 預設初始堆記憶體和最大堆記憶體如下:

最大堆記憶體為實體記憶體的 1/2,初始堆記憶體為實體記憶體的 1/64,但當初始堆記憶體至少為 8MB 時,取 8MB。

當預設空閑堆記憶體低於 40% 時,JVM 會增大堆直到 -Xmx 上限;當空閑堆記憶體超過 70% 時,JVM 會縮小堆直到 -Xms 下限。
因此,伺服器通常將 -Xms 和 -Xmx 設定為相同值,避免每次 GC 後重新調整堆大小。對象的堆記憶體由稱為記憶體回收行程的自動記憶體管理系統回收。

最大堆記憶體是 JVM 記憶體使用量的上限,實際運行過程中按需使用。預設分配給年輕代的最大空間為總堆大小的 1/3。

Spring Boot 針對最初的問題,如果每個程式都按預設配置啟動,當一台伺服器部署多個應用時就會出現記憶體不足,造成一定浪費。最簡單的做法是在執行 java -jar 啟動時添加相應的 JVM 記憶體參數。
java -Xms64m -Xmx128m -jar xxx.jar
專案使用 Docker 部署,先看一下原始 Dockerfile 檔案:

EXPOSE 8061

ADD target/certif-system-2.1.0.jar /certif-system-2.1.0.jar

ENTRYPOINT ["java", "-jar","/certif-system-2.1.0.jar"]

Spring Boot 最佳化
限制 JVM 記憶體

Spring Boot 會將環境變數傳遞給應用——但 JAVA_OPTS 不是給應用用的,而是給 Java 運行時本身用的。因此需要用 $JAVA_OPTS 變數配合 exec java。這需要對 Dockerfile 做小幅修改:
ENTRYPOINT exec java $JAVA_OPTS -jar app.jar
運行 docker run 命令
表示運行時通過 -e 覆蓋環境變數中的 JAVA_OPTS 參數。
docker run -e JAVA_OPTS='-Xmx1344M -Xms1344M -Xmn448M -XX:MaxMetaspaceSize=192M -XX:MetaspaceSize=192M'

java.security.egd 的作用
SecureRandom 廣泛應用於 Java 各組件,可可靠地產生隨機數。但在需要大量產生隨機數的情境下,效能較低。此時可使用 "-Djava.security.egd=file:/dev/./urandom" 加速隨機數產生過程。
建議在大量使用隨機數時將隨機數產生器指定為 /dev/./urandom。
請注意下方原始碼第四行導致 bug 的原因。如果 java.security.egd 參數指定 file:/dev/random 或 file:/dev/urandom,會調用無參的 NativeSeedGenerator 建構函式,該無參建構函式預設使用 file:/dev/random。openjdk 的代碼與 hotspot 不同,openjdk 在後續產生隨機數時不使用此變數。

相關文章

探索更多特惠

  1. 簡訊服務(SMS)與郵件服務

    5萬封郵件套餐包低至USD 1.99,120條簡訊低至USD 1.00

phone 聯絡我們
Hi, I'm Alibaba Cloud AI Assistant!
I can help with questions and solutions.