91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Docker環境下Spring Boot應用內存飆升的原因

發布時間:2021-08-24 15:07:05 來源:億速云 閱讀:330 作者:chen 欄目:開發技術

本篇內容介紹了“Docker環境下Spring Boot應用內存飆升的原因”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

目錄
  • Spring Boot應用內存飆升

    • 服務現狀

    • JVM默認內存設置

  • 優化

    • 限制JVM內存

    • 參數解釋

      • JVM常見參數

      • java.security.egd 作用

    • 優化后的Dockerfile文件

      • 優化后的效果

        • JVM參數設置是否生效

      • 基礎鏡像優化

        • OpenJ9

        • GraalVM

        • Fabric8

      • 優化后的Dockerfile文件

        • 優化后的效果

        • 備注

          • Xmx < limit

            • 支持springboot多環境和jvm動態配置的Dockerfile


          Docker環境下Spring Boot應用內存飆升的原因

          Spring Boot應用內存飆升

          一個簡單的Spring Boot應用, 幾乎只有一個用戶在用,內存竟然達到1.2G, 可怕

          Docker環境下Spring Boot應用內存飆升的原因

          服務現狀

          由于之前服務比較少,服務器資源充足,許多服務啟動時都未添加JVM參數(遺留問題)。結果就是每個服務啟動都占用了1.2G-2G的內存,有些服務的體量根本用不了這么多。那么,在Spring Boot中如果未設置JVM內存參數時,JVM內存是如何配置的呢?

          JVM默認內存設置

          當運行一個Spring Boot項目時,如果未設置JVM內存參數,Spring Boot默認會采用JVM自身默認的配置策略。在資源比較充足的情況下,開發者倒是不太用關心內存的設置。但一旦涉及到資源不足,JVM優化,那么就需要了解默認的JVM內存配置策略。

          關于JVM內存最常見的設置為初始堆大小(-Xms)和最大堆內存(-Xmx)。很多人懶得去設置,而是采用JVM的默認值。特別是在開發環境下,如果啟動的微服務比較多,內存會被撐爆。

          而JVM默認內存配置策略分兩種場景,大內存空間場景和小內存空間場景(小于192M)。

          以4GB內存為例,初始堆內存大小和最大堆內存大小如下圖:

          Docker環境下Spring Boot應用內存飆升的原因

          默認情況下,最大堆內存占用物理內存的1/4,如果應用程序超過該上限,則會拋出OutOfMemoryError異常。初始堆內存大小為物理內存的1/64

          如果應用程序運行在手機上或物理內存小于192M時,JVM默認的初始堆內存大小和最大堆內存大小如下圖:

          Docker環境下Spring Boot應用內存飆升的原因

          最大堆內存為物理內存的1/2,初始堆內存大小為物理內存的1/64,但當初始堆內存最小為8MB,則為8MB。

          默認空余堆內存小于40%時,JVM就會增大堆直到-Xmx的最大限制;空余堆內存大于70%時,JVM會減少堆直到 -Xms的最小限制。

          因此,服務器一般設置-Xms、-Xmx相等以避免在每次GC后調整堆的大小。對象的堆內存由稱為垃圾回收器的自動內存管理系統回收。

          其中最大堆內存是JVM使用內存的上限,實際運行過程中使用多少便是多少。默認,分配給年輕代的最大空間量是堆總大小的三分之一。

          針對最開始的問題,如果每個程序都按照默認配置啟動,一臺服務器上部署多個應用時,就會出現內存吃緊的情況,造成一定的浪費。最簡單的操作就是在執行java -jar啟動時添加上對應的jvm內存設置參數。

          java -Xms64m -Xmx128m -jar xxx.jar

          項目使用的是Docker部署, 我們先來查看 原來的Dockerfile文件

          確實沒有設置-Xms、-Xmx

          #設置鏡像基礎,jdk8
          FROM java:8
          #維護人員信息
          MAINTAINER FLY
          #設置鏡像對外暴露端口
          EXPOSE 8061
          #將當前 target 目錄下的 jar 放置在根目錄下,命名為 app.jar,推薦使用絕對路徑。
          ADD target/certif-system-2.1.0.jar /certif-system-2.1.0.jar
          # 時區設置
          RUN echo "Asia/shanghai" > /etc/timezone
          #執行啟動命令
          ENTRYPOINT ["java", "-jar","/certif-system-2.1.0.jar"]

          優化

          限制JVM內存

          #設置變量 JAVA_OPTS
           
          ENV JAVA_OPTS=""#這樣寫會以shell方式執行,會替換變量
           
          ENTRYPOINT java ${JAVA_OPTS}-Djava.security.egd=file:/dev/./urandom -jar /app.jar
           
          #下面這樣寫法不行,他只是拼接不會識別變量
           
          #ENTRYPOINT ["java","${JAVA_OPTS}","-Djava.security.egd=file:/dev/./urandom","-jar","app.jar"]

          Spring Boot會將任何環境變量傳遞給應用程序 - 但是我們的JAVA_OPTS并非是針對應用程序的,而是針對Java runtime本身的。 所以我們需要使用$ 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'

          參數解釋

          JVM常見參數

          可通過JAVA_OPTS設置

          參數說明:
          -server:一定要作為第一個參數,在多個CPU時性能佳
          -Xms:初始Heap大小,使用的最小內存,cpu性能高時此值應設的大一些
          -Xmx:java heap最大值,使用的最大內存
          -XX:PermSize:設定內存的永久保存區域
          -XX:MaxPermSize:設定最大內存的永久保存區域
          -XX:MaxNewSize:
          +XX:AggressiveHeap 會使得 Xms沒有意義。這個參數讓jvm忽略Xmx參數,瘋狂地吃完一個G物理內存,再吃盡一個G的swap。
          -Xss:每個線程的Stack大小
          -verbose:gc 現實垃圾收集信息
          -Xloggc:gc.log 指定垃圾收集日志文件
          -Xmn:young generation的heap大小,一般設置為Xmx的3、4分之一
          -XX:+UseParNewGC :縮短minor收集的時間
          -XX:+UseConcMarkSweepGC :縮短major收集的時間
          提示:此選項在Heap Size 比較大而且Major收集時間較長的情況下使用更合適。

          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在后續產生隨機數的時候沒有使用這個變量。

          abstract class SeedGenerator {
          ......
              static {
                  String egdSource = SunEntries.getSeedSource();
                  if (egdSource.equals(URL_DEV_RANDOM) || egdSource.equals(URL_DEV_URANDOM)) {
                      try {
                          instance = new NativeSeedGenerator();
                          if (debug != null) {
                              debug.println("Using operating system seed generator");
                          }
                      } catch (IOException e) {
                          if (debug != null) {
                              debug.println("Failed to use operating system seed "
                                            + "generator: " + e.toString());
                          }
                      }
                  } else if (egdSource.length() != 0) {
                      try {
                          instance = new URLSeedGenerator(egdSource);
                          if (debug != null) {
                              debug.println("Using URL seed generator reading from "
                                            + egdSource);
                          }
                      } catch (IOException e) {
                          if (debug != null)
                              debug.println("Failed to create seed generator with "
                                            + egdSource + ": " + e.toString());
                      }
                  }
          ......
              }

          優化后的Dockerfile文件

          #設置基礎鏡像jdk8
          FROM java:8
          #維護人員信息
          MAINTAINER FLY
          #設置鏡像對外暴露端口
          EXPOSE 8061
          #將當前 target 目錄下的 jar 放置在根目錄下,命名為 app.jar,推薦使用絕對路徑。
          ADD target/certif-system-2.1.0.jar /certif-system-2.1.0.jar
          # 設置環境變量
          ENV JAVA_OPTS="-server -Xms512m -Xmx512m"
          # 時區設置
          RUN echo "Asia/shanghai" > /etc/timezone
          #執行啟動命令
          #ENTRYPOINT ["java", "-jar","/certif-system-2.1.0.jar"]
          ENTRYPOINT exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar /certif-system-2.1.0.jar

          優化后的效果

          Docker環境下Spring Boot應用內存飆升的原因

          JVM參數設置是否生效

          通過 docker exec -it 5a8ff3925974 ps -ef | grep java 查看

          CONTAINER ID   NAME             CPU %     MEM USAGE / LIMIT   MEM %     NET I/O           BLOCK I/O         PIDS
          5a8ff3925974   certif-system    0.74%     493.3MiB / 800MiB   61.66%    272kB / 304kB     7.54MB / 0B       97
           
           
          [root@localhost certif]# docker exec -it 5a8ff3925974 ps -ef | grep java
          root           1       0  5 12:13 ?        00:01:02 java -server -Xms512m -Xmx51

          Docker環境下Spring Boot應用內存飆升的原因

          基礎鏡像優化

          減少Spring Boot減少JVM占用的三種Dockerfile鏡像配置:

          OpenJ9

          OpenJ9:取代Hotspot的IBM Eclipse項目。它已經被開發很長一段時間,看起來已經足夠成熟,可以用于生產。您可以立即輕松地獲益,替換一些基本鏡像和一些參數可能足以為您的應用程序提供巨大的推動力 - 我已經通過更改 Dockerfile基本映像替換了一些應用程序,節約了大約 1/3的內存占用,增強了吞吐量。

          FROM adoptopenjdk/openjdk8-openj9:alpine-slim
          COPY target/app.jar /my-app/app.jar
          ENTRYPOINT java $JAVA_OPTS -Xshareclasses -Xquickstart -jar /my-app/app.jar
          GraalVM

          GraalVM:圍繞這個由Oracle實驗室開發的有前途的虛擬機進行了大量宣傳。它為您提供了將應用程序編譯為本機鏡像的選項,生成鏡像非常非常快且內存消耗很少,吸引人眼球的另一個功能是能夠與多種語言(如Javascript,Ruby,Python和Java)進行交互操作。

          FROM oracle/graalvm-ce:1.0.0-rc15
          COPY target/app.jar /my-app/app.jar
          ENTRYPOINT java $JAVA_OPTS -jar /my-app/app.jar
          Fabric8

          Fabric8 shell:一個bash腳本,可根據應用程序當前運行環境自動為您配置JVM參數。它可以在這里下載,是這個研究項目的產物。它降低了不少內存:

          FROM java:openjdk-8-alpine
          COPY target/app.jar /my-app/app.jar
          COPY run-java.sh /my-app/run-java.sh
          ENTRYPOINT JAVA_OPTIONS=${JAVA_OPTS} JAVA_APP_JAR=/my-app/app.jar /my-app/run-java.sh

          雖然我們在應用解決方案時總是需要考慮上下文,但對我來說,獲勝者是OpenJ9,從而以最少的配置實現了生產就緒的性能和內存占用。

          雖然仍然沒有找到使用不合適的情況,但這并不意味著它將成為一個銀彈解決方案,請記住,最好是測試替代品,看看哪種更適合您的需求。

          優化后的Dockerfile文件

          #設置鏡像基礎,jdk8
          FROM adoptopenjdk/openjdk8-openj9:alpine-slim
          #維護人員信息
          MAINTAINER FLY
          #設置鏡像對外暴露端口
          EXPOSE 8061
          #將當前 target 目錄下的 jar 放置在根目錄下,命名為 app.jar,推薦使用絕對路徑。
          ADD target/certif-system-2.1.0.jar /certif-system-2.1.0.jar
          # 設置環境變量
          ENV JAVA_OPTS="-server -Xms512m -Xmx512m"
          # 時區設置
          RUN echo "Asia/shanghai" > /etc/timezone
          #執行啟動命令
          #ENTRYPOINT ["java", "-jar","/certif-system-2.1.0.jar"]
          #ENTRYPOINT exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar /certif-system-2.1.0.jar
          #ENTRYPOINT java $JAVA_OPTS -Xshareclasses -Xquickstart -jar /certif-system-2.1.0.jar
          ENTRYPOINT java $JAVA_OPTS -Xshareclasses -Xquickstart -jar /certif-system-2.1.0.jar

          優化后的效果

          Docker環境下Spring Boot應用內存飆升的原因

          備注

          Xmx < limit

          docker鏡像的內存上限,不能全部給“-Xmx”。因為JVM消耗的內存不僅僅是Heap,如下圖:

          JVM基礎結構如下:棧、堆。

          Docker環境下Spring Boot應用內存飆升的原因


          JVM中的棧主要是指線程里面的棧,里面有方法棧、native方法棧、PC寄存器等等;每個方法棧是由棧幀組成的;每個棧幀是由局部變量表、操作數棧等組成。

          每個棧幀其實就代表一個方法


          java中所有對象都在堆中分配;堆中對象又分為年輕代、老年代等等,不同代的對象使用不同垃圾回收算法。

          -XMs:啟動虛擬機預留的內存 -Xmx:最大的堆內存

          因此

          JVM = Heap + Method Area + Constant Pool + Thread Stack * num of thread
          所以Xmx的值要小于鏡像上限內存。

          支持springboot多環境和jvm動態配置的Dockerfile

          假設springboot項目 myboot-api , 在其根目錄下創建文件Dockerfile
          內容如下:

          FROM java:8
          MAINTAINER xxx
          RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
          RUN echo 'Asia/Shanghai' >/etc/timezone
          ENV LANG=zh_CN.UTF-8 \
          	JAVA_OPTS="-server -Xms512m -Xmx512m" \
              SPRING_PROFILES_ACTIVE="dev"
          #ARG JAR_FILE
          #ADD ${JAR_FILE} app.jar
          ADD target/myboot-api.jar app.jar
          ENTRYPOINT exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar -Dspring.profiles.active=${SPRING_PROFILES_ACTIVE} /app.jar

          其中ENV 環境變量

          JAVA_OPTS JVM堆內存起始最大值配置
          SPRING_PROFILES_ACTIVE application.yml環境

          Linux 命令行創建鏡像 啟動容器

          echo "===============動態參數配置 begin===============>"
          APPLICATION_NAME=xxx-srm-api
          echo "image and container name is $APPLICATION_NAME"
           
          # springboot啟動的端口號
          BootPort=8082
          echo "the spring boot ($APPLICATION_NAME) port is $BootPort"
           
          # docker中的springboot啟動的端口號
          DockerBootPort=8082
           
          echo "===============動態參數配置 end===============>"
          echo "build docker image"
          # mvn dockerfile:build
          docker build -f Dockerfile -t $APPLICATION_NAME:latest .
           
          echo "current docker images:"
          docker images | grep $APPLICATION_NAME
           
          echo "start container ===============> "
          docker run -d -p $BootPort:$DockerBootPort -e JAVA_OPTS="-server -Xms512m -Xmx512m" -e SPRING_PROFILES_ACTIVE="test"  --name $APPLICATION_NAME $APPLICATION_NAME:latest

          “Docker環境下Spring Boot應用內存飆升的原因”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

          向AI問一下細節

          免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

          AI

          内江市| 寿光市| 方正县| 兰州市| 峡江县| 宁河县| 托克托县| 大宁县| 兴化市| 隆安县| 合肥市| 合水县| 五莲县| 佛坪县| 东台市| 菏泽市| 金沙县| 宽城| 阿拉善盟| 东阳市| 连云港市| 偃师市| 朝阳县| 清丰县| 沂水县| 濉溪县| 翼城县| 武邑县| 金山区| 连南| 兴宁市| 新干县| 克山县| 乌什县| 琼海市| 上虞市| 涞源县| 疏附县| 克东县| 石棉县| 印江|