加油
努力

为什么刚部署的Spring Boot应用就占用了较多内存?

刚部署的 Spring Boot 应用占用较多内存是常见现象,主要原因包括以下几个方面:


1. JVM 默认堆内存设置较大

Spring Boot 底层运行在 JVM 上,而 JVM 的默认堆内存大小会根据物理内存自动调整。例如:

  • 如果服务器有 8GB 内存,JVM 可能默认分配 1/4(即 2GB)作为最大堆(-Xmx)。
  • 即使应用实际使用很少,操作系统也会看到较高的“虚拟”内存占用。

解决方案
手动限制 JVM 堆内存,例如:

java -Xms256m -Xmx512m -jar your-app.jar

或通过环境变量:

export JAVA_OPTS="-Xms256m -Xmx512m"
java $JAVA_OPTS -jar your-app.jar

2. Spring Boot 自动配置和依赖加载

Spring Boot 遵循“约定优于配置”,会自动加载大量 Starter 组件(如 Web、数据源、安全等),即使你没有使用它们。

  • 每个自动配置类都会被加载到内存中。
  • 所有 Bean 在启动时被创建并注册到 IoC 容器。
  • 使用了较多 Starter(如 spring-boot-starter-web, spring-boot-starter-data-jpa, spring-boot-starter-security 等)会显著增加内存开销。

优化建议

  • 移除不必要的 Starter 依赖。
  • 使用 @ConditionalOn... 注解或条件化配置减少无用 Bean。
  • 使用 --debug 启动参数查看哪些自动配置被启用。

3. 嵌入式服务器(如 Tomcat)的开销

Spring Boot 默认内嵌 Tomcat、Jetty 或 Undertow,这些服务器本身也需要内存来初始化线程池、连接器等组件。

  • Tomcat 默认启动多个工作线程(通常 10+)。
  • 即使没有请求,这些线程和相关对象也占用内存。

可选方案

  • 切换为更轻量的服务器(如 Undertow):
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
            </exclusion>
        </exclusions>
    </exclusion>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-undertow</artifactId>
    </dependency>
  • 调整服务器线程数:
    server.tomcat.threads.max=50
    server.tomcat.threads.min-spare=5

4. 类加载和元空间(Metaspace)占用

Spring Boot 应用包含大量类(尤其是使用了 Lombok、Spring Data JPA、Hibernate 等),这些类会被加载到 Metaspace 区域。

  • 元空间用于存储类的元数据,默认不限上限(受系统内存限制)。
  • 类越多,Metaspace 占用越高。

控制方式

-XX:MaxMetaspaceSize=256m

5. 日志框架和其他监控组件

如果引入了:

  • Spring Boot Actuator
  • Prometheus + Micrometer
  • 分布式追踪(如 Sleuth)
  • 日志框架(Logback、Log4j2)

这些组件在启动时会预加载大量类和线程,增加内存消耗。

建议

  • 生产环境按需开启 Actuator 端点。
  • 避免引入不必要的监控依赖。

6. 容器化部署中的认知偏差

在 Docker 中,docker stats 显示的内存可能包括:

  • JVM 堆外内存(Direct Memory、Code Cache、Thread Stacks)
  • Native 内存(JIT 编译、GC 等)

JVM 并不总是及时向操作系统释放内存,导致“看起来”占用高。

建议
使用 JDK 8u131+ 或 JDK 10+ 支持的容器感知内存:

-Djdk.lang.Process.launchMechanism=OOME -XX:+UseContainerSupport

并配合:

--memory=512m

在 Docker 中限制容器内存。


7. 冷启动与后续表现

刚启动时,Spring Boot 会进行大量反射、X_X生成(AOP)、Bean 初始化,导致短暂高内存使用。稳定后通常会下降。

观察建议
使用 jstatjconsoleVisualVMPrometheus + Micrometer 监控长时间运行后的实际内存趋势。


总结:如何降低内存占用?

措施 示例
限制 JVM 堆内存 -Xms256m -Xmx512m
减少不必要的 Starter 移除未使用的 starter-* 依赖
使用轻量 Web 服务器 改用 Undertow
控制 Metaspace -XX:MaxMetaspaceSize=256m
开启容器支持 -XX:+UseContainerSupport
关闭调试功能 设置 spring.main.banner-mode=off

🔍 诊断工具推荐

  • jps + jstat -gc <pid> 查看 GC 和内存分布
  • jmap -heap <pid> 查看堆详情
  • jcmd <pid> VM.native_memory(需开启)查看 native 内存
  • Spring Boot Actuator 的 /actuator/metrics/jvm.memory.used

如果你提供具体的内存占用数值、JVM 版本、依赖列表和启动参数,我可以进一步帮你分析优化。

云服务器