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 联系我们
你好,我是AI助理。
可以解答问题、推荐解决方案等。