易君召
发布于 2026-09-11 / 作者:易君召 / 1 阅读
0

Java 后端服务性能优化完整方案

优化遵循先定位瓶颈,再针对性优化原则:优先通过监控找到瓶颈(CPU、内存、IO、网络、锁、数据库),不要上来就盲目改代码。整体分为:应用层 (JVM + 代码)、中间件、数据库、架构、运维五大块。

一、JVM 调优(内存、GC)

1. 内存模型与参数

  • 优先使用 G1GC(JDK8 默认),高延迟场景用 ZGC(JDK11+/17+,低停顿);避免 CMS,已经废弃。

  • 核心原则:堆不要过大,堆越大 GC 停顿越长;生产堆一般4G~8G,根据业务调整。

-Xms4g -Xmx4g          # 固定堆,避免运行时扩容
-XX:+UseZGC            # JDK17推荐ZGC
-XX:MaxMetaspaceSize   # 限制元空间,防止类加载泄漏

不要把堆设置到宿主机内存的 80% 以上,预留 OS、缓冲区、堆外内存。

2. GC 优化手段

  1. 减少短生命周期对象:避免循环内创建对象、字符串拼接+(用StringBuilder

  2. 大对象控制:大对象直接进老年代,容易造成老年代碎片,尽量拆分

  3. 开启 GC 日志:方便事后分析,-Xlog(JDK17)/ -XX:+PrintGCDetails

  4. 工具:jstack线程栈、jmap堆 dump、jstatGC 统计、MAT分析堆泄漏

3. 常见 JVM 问题

  • 内存泄漏:静态集合持有对象、ThreadLocal 忘记 remove、流 / 连接未关闭

  • 频繁 FullGC:内存泄漏、老年代空间不足、大对象冲击

  • 线程阻塞:死锁、锁竞争、IO 阻塞

二、代码层面优化(成本最低,见效快)

1. 对象与集合

  1. 集合初始化预估容量:new HashMap<>(1024),减少扩容 rehash;HashMap 默认负载因子 0.75

  2. 优先使用基本类型:int代替Integer,减少自动装箱拆箱;大量数值场景用Primitive集合(Eclipse Collections)

  3. 循环优化:循环外定义变量,不要在循环内做查询、IO、对象创建

  4. 字符串:大量拼接用StringBuilder;常量字符串放常量池;避免String.substring老版本内存泄漏(JDK8 已修复)

  5. 避免不必要的序列化:JSON 序列化是 CPU 大户,按需选择序列化器(Jackson > Fastjson2),减少序列化字段

2. 并发与锁优化(高并发核心)

  1. 减少锁粒度:分段锁,不要锁整个对象;优先ReentrantLock可中断 / 可超时,读多写少用ReentrantReadWriteLock

  2. 无锁优先:乐观锁CASAtomicLong),适合竞争不大场景;竞争激烈 CAS 会大量自旋,反而浪费 CPU

  3. 避免 synchronized 锁膨胀:锁对象不要用字符串常量、包装类;锁对象尽量不变化

  4. 线程池正确使用:

    • 不要new Thread(),统一线程池管理

    • IO 密集型:核心线程数 CPU核心数 * 2 ~ 20;CPU 密集型:CPU核心数+1

    • 必须指定拒绝策略,防止任务堆积 OOM;监控队列长度

// 示例:线程池
ExecutorService pool = new ThreadPoolExecutor(
    8, 16, 60L, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(200),
    new ThreadPoolExecutor.CallerRunsPolicy()
);
  1. ThreadLocal:用完remove(),防止内存泄漏

3. IO 优化

  • 文件 / 网络流:try-with-resources自动关闭资源,防止句柄泄漏

  • 同步 IO 阻塞严重场景:改用异步非阻塞(Netty、Spring WebFlux)

  • 减少远程调用次数:批量接口代替循环单条查询

三、数据库优化(绝大多数 Java 服务瓶颈在 DB)

1. SQL 与索引

  1. 避免索引失效:like '%xxx'、函数操作字段、隐式类型转换、or、order by/group by 无索引

  2. 索引设计:联合索引最左前缀原则;不要建过多索引(写会变慢);区分度低字段不适合索引

  3. 禁止select *,只查需要字段;分页大偏移量不要limit 100000,10,改用主键分页:where id > 100000 limit 10

  4. 避免大事务:事务越长锁持有时间越久,并发下降;拆分长事务

  5. 禁止锁表语句;避免for update滥用

2. 连接池

  • MyBatis/JPA 底层连接池:HikariCP(首选,性能最好)

  • 合理配置最大连接数,不是越大越好。数据库连接数上限有限,连接过多会导致数据库上下文切换飙升。

经验公式:连接数 ≈ ((核心数 * 2) + 磁盘数),根据慢查询调整

3. 缓存分层(减少 DB 压力)

  1. 本地缓存(Caffeine):热点数据,低延迟;进程内,多实例数据不一致,适合可短暂不一致数据

  2. 分布式缓存(Redis):全局热点;注意缓存穿透、击穿、雪崩

    • 穿透:不存在 key,缓存 + DB 都查 → 布隆过滤器 / 缓存空值(短期过期)

    • 击穿:热点 key 过期 → 互斥锁 / 永不过期

    • 雪崩:大量 key 同时过期 → 过期时间加随机偏移

  3. 缓存更新策略:Cache Aside(读多写少首选);写数据库,删除缓存,不要更新缓存

四、中间件优化

  1. Redis

    • 使用短 key;减少大 value(>10KB 尽量拆分);合理序列化

    • 尽量使用 pipeline 批量命令代替多次网络往返;避免keys命令,用scan

    • 集群分片,避免单节点热点;读写分离

  2. MQ(RocketMQ/Kafka)

    • 批量发送、批量消费;合理设置批次大小

    • 消息体不要过大;异步发送

    • 分区数 = 消费线程数,提升并行消费能力

五、架构层面优化(业务量大时才需要)

  1. 读写分离:主库写,从库读,分担查询压力

  2. 分库分表:数据量千万级以上,按业务分片(ShardingSphere)

  3. 异步化:非主流程逻辑异步(消息队列、Spring @Async),缩短接口 RT。例如:日志、推送、统计不要同步执行

  4. 服务拆分:单体拆微服务,隔离资源;但微服务会带来网络开销,不要过度拆分

  5. 限流、熔断、降级(Sentinel):保护服务,防止雪崩;限制 QPS,高峰期降级非核心接口

  6. CDN + 对象存储:静态资源不要走 Java 后端

六、框架层面优化(SpringBoot)

  1. SpringBoot:关闭不需要的自动配置;懒加载 bean

  2. MyBatis:关闭不必要日志;一级缓存按需使用;避免 N+1 查询

  3. Spring MVC:

    • 接口返回分页,不一次性返回全量数据

    • 文件上传限制大小;异步处理大文件

  4. 序列化:Jackson 配置,忽略不需要字段,关闭默认格式化

七、性能观测工具(定位瓶颈)

类别

工具

JVM

jstack、jmap、jstat、MAT、Arthas(阿里在线诊断,推荐)

链路追踪

SkyWalking、Pinpoint、Jaeger(看接口 RT、慢 SQL、远程调用耗时)

压测

JMeter、Gatling、wrk

数据库

explain、slowlog、mysql performance_schema

Arthas 非常实用:在线看方法耗时、线程状态、类加载,不用重启服务。

八、优化优先级(很重要)

  1. 数据库慢 SQL、缺少索引(最高频瓶颈,优先查)

  2. 锁竞争、线程阻塞、长事务

  3. 缓存缺失 / 缓存策略错误

  4. JVM 内存泄漏、频繁 GC

  5. 代码冗余、循环远程调用

  6. 架构改造(分库分表、读写分离,成本最高,最后考虑)

九、避坑清单

  • ❌ 不要上来就调 JVM 参数,先找到瓶颈

  • ❌ 线程池不是越大越快;数据库连接数不是越大越好

  • ❌ 缓存不是越多越好,要考虑一致性、内存占用

  • ❌ 异步化不要滥用:异步会增加复杂度,无法简单捕获异常

  • ❌ 避免过早优化:先保证业务正确,再优化热点路径