在 Linux 服务器上优化 Spring Boot 服务的资源占用(CPU、内存、线程、文件描述符等),需从 JVM 层、Spring Boot 配置、Linux 系统层及应用代码多维度协同调优。以下是经过生产验证的实用建议,按优先级和影响程度分类整理:
✅ 一、JVM 层优化(最核心)
-
选择合适的 JDK 版本与 GC 策略
- ✅ 推荐使用 JDK 17+(LTS)或 JDK 21(推荐),避免 JDK 8(缺乏 ZGC/Shenandoah、容器感知弱)。
- ✅ 小内存(≤2GB)服务:启用
ZGC(低延迟)或G1GC(平衡型)# 示例(JDK 17+,堆 1.5G,ZGC) -Xms1536m -Xmx1536m -XX:+UseZGC -XX:+UnlockExperimentalVMOptions -XX:+UseContainerSupport # 必须!让 JVM 识别 Docker/cgroup 内存限制 -XX:MaxRAMPercentage=75.0 # 容器环境推荐用百分比而非绝对值 -XX:InitialRAMPercentage=50.0 -XX:+AlwaysPreTouch # 启动时预触内存页,减少运行时缺页中断 - ⚠️ 避免
-XX:+UseParallelGC(吞吐量高但停顿长)或-XX:+UseSerialGC(单线程,仅限极小场景)。
-
合理设置堆内存(避免过大/过小)
- 堆大小建议:
物理内存 × 60%~75%(预留空间给 Metaspace、Direct Memory、OS 缓存等) - ❌ 禁止
Xms != Xmx(防止动态扩容抖动);✅ 强制固定大小(如-Xms2g -Xmx2g) - Metaspace:
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m(防动态增长导致 OOM)
- 堆大小建议:
-
关闭非必要 JVM 功能
-XX:-UseBiasedLocking # JDK 15+ 默认禁用,旧版显式关闭 -XX:+DisableExplicitGC # 禁止 System.gc()(尤其避免 Netty 的 ReferenceCounted 调用) -Dsun.zip.disableMemoryMapping=true # 防止 Jar 包解压映射占用虚拟内存
✅ 二、Spring Boot 应用层优化
-
精简依赖 & 关闭自动配置
- ✅ 使用
spring-boot-starter-web替代spring-boot-starter-webflux(除非真需要响应式) - ✅ 在
application.yml中按需排除无用 AutoConfiguration:spring: autoconfigure: exclude: - org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration - org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration - org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration - ✅ 移除未使用的 Starter(如
spring-boot-starter-actuator仅保留必要 endpoints):management: endpoints: web: exposure: include: "health,metrics,prometheus" # 关闭 env, beans, threaddump 等敏感/高开销端点
- ✅ 使用
-
Web 容器调优(Tomcat/Jetty/Undertow)
- ✅ 首选 Undertow(内存占用最低,性能高):
<!-- pom.xml --> <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> - ✅ Tomcat 调优(若必须用):
server: tomcat: max-connections: 5000 accept-count: 100 max-threads: 200 min-spare-threads: 10 connection-timeout: 5000
- ✅ 首选 Undertow(内存占用最低,性能高):
-
连接池与异步线程池
- ✅ HikariCP(数据库连接池)最小化配置:
spring: datasource: hikari: minimum-idle: 5 maximum-pool-size: 20 # 根据 DB 连接数上限调整 idle-timeout: 300000 max-lifetime: 1800000 connection-timeout: 5000 - ✅ 自定义
@Async线程池(避免使用SimpleAsyncTaskExecutor):@Bean("taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(4); executor.setMaxPoolSize(8); executor.setQueueCapacity(100); executor.setThreadNamePrefix("async-"); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); return executor; }
- ✅ HikariCP(数据库连接池)最小化配置:
✅ 三、Linux 系统层优化
-
进程资源限制(systemd 或 ulimit)
- ✅ systemd 服务配置(
/etc/systemd/system/myapp.service):[Service] MemoryLimit=2G # 硬限制,超限会被 OOM killer 杀死 CPUQuota=80% # 限制 CPU 使用率(可选) LimitNOFILE=65536 # 文件描述符数(关键!) LimitNPROC=4096 # 进程/线程数限制 TasksMax=4096 # 防止 fork 爆炸 Restart=on-failure RestartSec=10🔁 执行
sudo systemctl daemon-reload && sudo systemctl restart myapp
- ✅ systemd 服务配置(
-
内核参数调优(针对高并发)
# /etc/sysctl.conf net.core.somaxconn = 65535 net.core.netdev_max_backlog = 5000 net.ipv4.tcp_max_syn_backlog = 65535 net.ipv4.ip_local_port_range = 1024 65535 fs.file-max = 2097152 kernel.pid_max = 65536执行
sudo sysctl -p生效 -
日志与监控轻量化
- ✅ 使用
logback-spring.xml降低日志开销:<configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> <immediateFlush>false</immediateFlush> <!-- 批量刷盘 --> </encoder> </appender> <!-- 关闭 DEBUG 日志(生产环境严禁) --> <root level="INFO"> <appender-ref ref="CONSOLE"/> </root> </configuration> - ✅ Prometheus + Micrometer:只暴露必要指标(禁用
jvm.memory.*等高频指标,改用jvm.memory.used)
- ✅ 使用
✅ 四、代码与架构层面(长效降本)
| 场景 | 优化方案 |
|---|---|
| JSON 序列化 | ✅ 用 Jackson 替代 Gson(Spring Boot 3 默认);禁用 WRITE_DATES_AS_TIMESTAMPS |
| 大对象处理 | ✅ 流式读写(ObjectMapper.readValues())、避免 @RequestBody 接收超大 JSON(加 @Size(max=10240) 校验) |
| 缓存策略 | ✅ 本地缓存用 Caffeine(比 Guava 更省内存);分布式缓存用 Redis 时设置 TTL + LRU |
| 定时任务 | ✅ 用 @Scheduled(fixedDelay = 60000) 替代 cron(更可控);避免 @Async + while(true) |
| 健康检查 | ✅ /actuator/health 禁用数据库等远程依赖检查(show-details: never) |
🚫 常见反模式(务必规避)
- ❌ 在
@PostConstruct中执行耗时操作(阻塞启动,延长就绪时间) - ❌ 使用
static集合存储业务数据(内存泄漏高发区) - ❌
@EventListener监听ContextRefreshedEvent做重初始化(重复加载) - ❌ 未关闭
InputStream/OutputStream(文件描述符泄漏 →Too many open files) - ❌ 在 Controller 中直接
new Thread()(失控线程数 →Cannot create thread)
📊 监控与验证(上线前必做)
# 实时观察(推荐 atop / htop)
htop -u your_app_user
# 查看堆外内存(Native Memory Tracking)
java -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics ...
# 检查文件描述符
lsof -p $(pgrep -f "myapp.jar") | wc -l
# GC 日志分析(添加 JVM 参数)
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/log/myapp/gc.log
✅ 工具推荐:Prometheus + Grafana(监控 JVM 堆/非堆/线程/类加载)、Arthas(线上诊断)、jcmd(轻量触发堆 dump)
💡 总结口诀(便于记忆)
“JVM 固堆配 ZGC,容器支持不能少;
Undertow 代 Tomcat,自动配置要精砍;
连接池线程池,大小合适不浪费;
Linux 限资源,ulimit + sysctl;
代码避静态、关流、少循环,监控闭环才稳妥。”
如需进一步定制(如 Kubernetes 环境下的 HPA + JVM 配置联动、GraalVM 原生镜像编译),欢迎补充场景,我可提供专项方案。
PHPWP博客