优化遵循先定位瓶颈,再针对性优化原则:优先通过监控找到瓶颈(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 优化手段
减少短生命周期对象:避免循环内创建对象、字符串拼接
+(用StringBuilder)大对象控制:大对象直接进老年代,容易造成老年代碎片,尽量拆分
开启 GC 日志:方便事后分析,
-Xlog(JDK17)/-XX:+PrintGCDetails工具:
jstack线程栈、jmap堆 dump、jstatGC 统计、MAT分析堆泄漏
3. 常见 JVM 问题
内存泄漏:静态集合持有对象、ThreadLocal 忘记 remove、流 / 连接未关闭
频繁 FullGC:内存泄漏、老年代空间不足、大对象冲击
线程阻塞:死锁、锁竞争、IO 阻塞
二、代码层面优化(成本最低,见效快)
1. 对象与集合
集合初始化预估容量:
new HashMap<>(1024),减少扩容 rehash;HashMap 默认负载因子 0.75优先使用基本类型:
int代替Integer,减少自动装箱拆箱;大量数值场景用Primitive集合(Eclipse Collections)循环优化:循环外定义变量,不要在循环内做查询、IO、对象创建
字符串:大量拼接用
StringBuilder;常量字符串放常量池;避免String.substring老版本内存泄漏(JDK8 已修复)避免不必要的序列化:JSON 序列化是 CPU 大户,按需选择序列化器(Jackson > Fastjson2),减少序列化字段
2. 并发与锁优化(高并发核心)
减少锁粒度:分段锁,不要锁整个对象;优先
ReentrantLock可中断 / 可超时,读多写少用ReentrantReadWriteLock无锁优先:乐观锁
CAS(AtomicLong),适合竞争不大场景;竞争激烈 CAS 会大量自旋,反而浪费 CPU避免 synchronized 锁膨胀:锁对象不要用字符串常量、包装类;锁对象尽量不变化
线程池正确使用:
不要
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()
);
ThreadLocal:用完
remove(),防止内存泄漏
3. IO 优化
文件 / 网络流:try-with-resources自动关闭资源,防止句柄泄漏
同步 IO 阻塞严重场景:改用异步非阻塞(Netty、Spring WebFlux)
减少远程调用次数:批量接口代替循环单条查询
三、数据库优化(绝大多数 Java 服务瓶颈在 DB)
1. SQL 与索引
避免索引失效:
like '%xxx'、函数操作字段、隐式类型转换、or、order by/group by 无索引索引设计:联合索引最左前缀原则;不要建过多索引(写会变慢);区分度低字段不适合索引
禁止
select *,只查需要字段;分页大偏移量不要limit 100000,10,改用主键分页:where id > 100000 limit 10避免大事务:事务越长锁持有时间越久,并发下降;拆分长事务
禁止锁表语句;避免
for update滥用
2. 连接池
MyBatis/JPA 底层连接池:HikariCP(首选,性能最好)
合理配置最大连接数,不是越大越好。数据库连接数上限有限,连接过多会导致数据库上下文切换飙升。
经验公式:连接数 ≈ ((核心数 * 2) + 磁盘数),根据慢查询调整
3. 缓存分层(减少 DB 压力)
本地缓存(Caffeine):热点数据,低延迟;进程内,多实例数据不一致,适合可短暂不一致数据
分布式缓存(Redis):全局热点;注意缓存穿透、击穿、雪崩
穿透:不存在 key,缓存 + DB 都查 → 布隆过滤器 / 缓存空值(短期过期)
击穿:热点 key 过期 → 互斥锁 / 永不过期
雪崩:大量 key 同时过期 → 过期时间加随机偏移
缓存更新策略:Cache Aside(读多写少首选);写数据库,删除缓存,不要更新缓存
四、中间件优化
Redis
使用短 key;减少大 value(>10KB 尽量拆分);合理序列化
尽量使用 pipeline 批量命令代替多次网络往返;避免
keys命令,用scan集群分片,避免单节点热点;读写分离
MQ(RocketMQ/Kafka)
批量发送、批量消费;合理设置批次大小
消息体不要过大;异步发送
分区数 = 消费线程数,提升并行消费能力
五、架构层面优化(业务量大时才需要)
读写分离:主库写,从库读,分担查询压力
分库分表:数据量千万级以上,按业务分片(ShardingSphere)
异步化:非主流程逻辑异步(消息队列、Spring @Async),缩短接口 RT。例如:日志、推送、统计不要同步执行
服务拆分:单体拆微服务,隔离资源;但微服务会带来网络开销,不要过度拆分
限流、熔断、降级(Sentinel):保护服务,防止雪崩;限制 QPS,高峰期降级非核心接口
CDN + 对象存储:静态资源不要走 Java 后端
六、框架层面优化(SpringBoot)
SpringBoot:关闭不需要的自动配置;懒加载 bean
MyBatis:关闭不必要日志;一级缓存按需使用;避免 N+1 查询
Spring MVC:
接口返回分页,不一次性返回全量数据
文件上传限制大小;异步处理大文件
序列化:Jackson 配置,忽略不需要字段,关闭默认格式化
七、性能观测工具(定位瓶颈)
Arthas 非常实用:在线看方法耗时、线程状态、类加载,不用重启服务。
八、优化优先级(很重要)
数据库慢 SQL、缺少索引(最高频瓶颈,优先查)
锁竞争、线程阻塞、长事务
缓存缺失 / 缓存策略错误
JVM 内存泄漏、频繁 GC
代码冗余、循环远程调用
架构改造(分库分表、读写分离,成本最高,最后考虑)
九、避坑清单
❌ 不要上来就调 JVM 参数,先找到瓶颈
❌ 线程池不是越大越快;数据库连接数不是越大越好
❌ 缓存不是越多越好,要考虑一致性、内存占用
❌ 异步化不要滥用:异步会增加复杂度,无法简单捕获异常
❌ 避免过早优化:先保证业务正确,再优化热点路径