走啊走
奋斗

2核2G的服务器跑Spring Boot应用需要做哪些优化?

服务器价格表

2 核 2G(2 vCPU, 2GB RAM)的服务器对于 Spring Boot 应用来说属于资源非常紧张的配置。Spring Boot 默认配置通常是为更充裕的资源设计的,如果不进行针对性优化,很容易出现内存溢出(OOM)、CPU 飙升或响应缓慢的问题。

以下是针对该配置的详细优化方案,分为 JVM 调优应用代码/依赖优化系统级优化架构/部署策略 四个维度:

1. JVM 参数调优(最关键的一步)

这是最直接且见效最快的方法。你需要限制堆内存大小,防止 OOM,并调整垃圾回收器以适应小内存环境。

推荐启动参数

假设你的应用最大堆内存设为 512MB – 768MB(保留约 1GB 给操作系统和其他进程),可以使用以下参数:

# 设置初始堆内存和最大堆内存 (建议比例 1:1 或略小)
-Xms512m -Xmx512m 

# 或者更激进一点,留给系统更多空间
-Xms384m -Xmx512m

# 指定新生代大小 (Young Gen),减少 Full GC 频率
-XX:MaxMetaspaceSize=128m 
-XX:NewRatio=2 
-XX:SurvivorRatio=8

# 选择适合小内存的垃圾回收器
# G1GC 是 JDK 9+ 的默认,但在极小内存下可能开销较大
# Serial GC 在单核/小内存下效率最高,但会停顿;Parallel GC 适合多核
# 强烈建议使用 G1 或 ParallelGC,并开启 ZGC (如果 JDK 版本支持且内存允许)
# 对于 2G 机器,通常推荐使用 G1 或 ParallelGC
-XX:+UseG1GC 
# 或者使用 ParallelGC (通常对小内存更友好)
# -XX:+UseParallelGC

# 关键优化:关闭未使用的功能以节省内存
-XX:-OmitStackTraceInFastThrow # 减少异常处理时的内存开销
-XX:+DisableExplicitGC       # 禁止 System.gc() 调用,避免意外触发 Full GC
-XX:+HeapDumpOnOutOfMemoryError # 崩溃时自动 dump 堆栈,方便排查
-XX:HeapDumpPath=/tmp/heap_dump.hprof

# 日志优化 (避免日志文件占用过多磁盘 IO 和内存缓冲)
-Dspring.output.ansi.enabled=NEVER

核心逻辑解释:

  • -Xms-Xmx 必须相等:避免 JVM 在运行时动态扩容堆内存导致的性能抖动。
  • 控制 Metaspace:Spring Boot 加载大量类,元空间容易撑爆,需限制上限。
  • GC 选择:在 2GB 总内存下,尽量让 Heap 保持在 500MB 左右。如果 CPU 频繁满负荷,尝试切换到 ParallelGC;如果追求低延迟,用 G1GC 并调小 MaxGCPauseMillis

2. 应用层与依赖优化

Spring Boot 默认加载了很多非必要的组件,需要“瘦身”。

  • 移除不必要的 Starter

    • 检查 pom.xmlbuild.gradle,只引入真正需要的依赖。
    • 例如:如果不做缓存,去掉 spring-boot-starter-cache;如果不做消息队列,去掉相关依赖。
    • 特别注意spring-boot-starter-web 默认包含 Tomcat,如果不需要 Servlet 容器(如跑轻量级 Netty),可考虑替换为 Undertow(Undertow 内存占用通常比 Tomcat 低)。
      <!-- 将 Tomcat 替换为 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>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-undertow</artifactId>
      </dependency>
  • 禁用 DevTools

    • 生产环境务必确保 spring-boot-devtools 未启用,它会在后台监控文件变化,消耗额外资源。
  • 数据库连接池优化

    • 2G 内存下,HikariCP 默认配置可能偏大。需手动调整 maximum-pool-size(建议设为 5-10,不要超过 15)和 minimum-idle
    • 配置示例 (application.yml):
      spring:
        datasource:
          hikari:
            maximum-pool-size: 8
            minimum-idle: 2
            connection-timeout: 30000
            idle-timeout: 600000
            max-lifetime: 1800000
  • 关闭 Actuator 的非必要端点

    • 默认 Actuator 暴露很多监控信息,不仅占用内存还增加安全风险。仅开启 healthinfo,或在 management.endpoints.web.exposure.include 中明确指定。
  • 异步任务与线程池

    • 检查是否有大量的 @Async 任务。默认的线程池可能创建过多线程。自定义一个固定的 ThreadPoolTaskExecutor,限制核心线程数和最大线程数(例如 corePoolSize=5, maxPoolSize=10)。

3. 系统与 OS 级优化

操作系统层面的配置同样重要,因为 2G 内存容错率极低。

  • 开启 Swap(交换分区)

    • 虽然 Swap 会降低速度,但在物理内存耗尽时,它是防止进程被 OOM Killer 杀死的最后一道防线。
    • 建议分配 2GB – 4GB 的 Swap 空间。
    • 命令参考:fallocate -l 2G /swapfile -> chmod 600 /swapfile -> mkswap /swapfile -> swapon /swapfile
    • 调整 vm.swappiness 值(建议设为 10 或更低,让系统优先使用物理内存,仅在必要时才用 Swap):
      sysctl vm.swappiness=10
  • 文件描述符限制

    • Spring Boot 在高并发下需要大量文件句柄。修改 /etc/security/limits.conf
      * soft nofile 65535
      * hard nofile 65535
  • 内核参数优化

    • 调整 TCP 连接相关的参数,优化网络吞吐:
      # 增加端口范围
      net.ipv4.ip_local_port_range = 1024 65535
      # 缩短 TIME_WAIT 时间
      net.ipv4.tcp_tw_reuse = 1
      net.ipv4.tcp_fin_timeout = 30

4. 架构与部署策略

如果上述优化后性能仍不达标,可能需要从架构层面入手。

  • 使用 Docker + 资源限制

    • 使用 Docker 运行,并严格限制容器资源,防止单个容器占满宿主机所有资源。
      docker run -d 
      --name my-app 
      --memory="1g" 
      --cpus="1.5" 
      -p 8080:8080 
      your-image:latest
    • 注意:Docker 限制为 1G,配合上面的 JVM 参数(-Xmx512m),留有余地给 JVM 元空间和系统开销。
  • 读写分离与缓存

    • 引入 Redis(如果使用外部 Redis 服务)作为缓存,大幅降低数据库压力。
    • 如果是单机部署,考虑使用 Caffeine 等本地缓存,但要注意内存占用。
  • 静态资源分离

    • 前端静态资源(JS/CSS/图片)应托管到 Nginx 或 CDN,不要让 Spring Boot 处理这些请求。
  • 水平扩展(最彻底的方案)

    • 如果业务确实增长,2 核 2G 的物理瓶颈很难突破。最好的办法是部署两个 1 核 1G 的实例,前面加一个 Nginx 做负载均衡。虽然成本增加,但稳定性远高于死磕单机优化。

总结 Checklist

  1. JVM: 强制 -Xms=-Xmx (约 512M),使用 G1/Parallel GC,限制 Metaspace。
  2. Web 容器: 考虑切换为 Undertow。
  3. DB: 调小 HikariCP 连接池大小。
  4. OS: 配置 Swap 分区,调整 swappiness
  5. Docker: 限制容器内存上限。
  6. 监控: 部署 Prometheus + Grafana 或简单的 top/jstat 监控,观察 GC 频率和内存曲线。

建议执行顺序:先调整 JVM 参数 -> 再清理依赖 -> 最后调整 OS 配置。上线前务必进行压测,观察是否会出现频繁的 Full GC。