性能调优

Spring Boot 应用的性能调优涉及 JVM 层、应用层、框架层三个维度。JVM 底层原理参见 JVM垃圾回收


JVM 内存配置

Spring Boot 应用本质是运行在 JVM 上的进程,内存分区详见 JVM 内存结构

堆内存

# 生产环境建议 Xms = Xmx,避免动态扩容带来的停顿
java -Xms2g -Xmx2g -jar app.jar
 
# 容器环境(Docker/K8s)使用比例,避免超出容器限制
java -XX:InitialRAMPercentage=50.0 \
     -XX:MaxRAMPercentage=75.0 \
     -jar app.jar

元空间(Metaspace)

存放类元数据,无上限时可能持续增长(动态代理、JSP 场景):

-XX:MetaspaceSize=256m       # 初始大小,达到后触发 GC
-XX:MaxMetaspaceSize=512m    # 上限,防止无限增长

详见 JVM - 方法区与元空间

堆外内存(Direct Memory)

NIO、Netty 等使用堆外内存,不受 -Xmx 限制:

-XX:MaxDirectMemorySize=512m

垃圾回收器选择

详见 垃圾回收 - 垃圾收集器

场景推荐收集器参数
低延迟(API 服务)G1-XX:+UseG1GC
超低延迟(< 1ms)ZGC-XX:+UseZGC
高吞吐(批处理)Parallel-XX:+UseParallelGC
小内存(< 256m)Serial-XX:+UseSerialGC

G1 调优(Spring Boot 默认推荐)

-XX:+UseG1GC
-XX:MaxGCPauseMillis=200          # 目标停顿时间
-XX:G1HeapRegionSize=8m           # Region 大小(1~32m,2 的幂)
-XX:G1NewSizePercent=20           # 新生代最小占比
-XX:G1MaxNewSizePercent=40        # 新生代最大占比
-XX:InitiatingHeapOccupancyPercent=45  # 触发并发标记的堆占用阈值

ZGC(Java 17+ Spring Boot 3.x 推荐)

-XX:+UseZGC
-XX:SoftMaxHeapSize=6g            # 软上限,GC 尽量不超过此值
-XX:ZCollectionInterval=5         # 定期 GC 间隔(秒)

GC 日志与分析

# 开启 GC 日志(Java 9+)
-Xlog:gc*:file=logs/gc.log:time,uptime,level,tags:filecount=5,filesize=20m
 
# 打印 GC 详情(Java 8)
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:logs/gc.log

GC 日志分析工具:

  • GCEasy:在线分析,可视化 GC 停顿、吞吐量、内存趋势
  • GCViewer:桌面工具,离线分析
  • Actuator /actuator/metrics:查看 jvm.gc.pause 指标,详见 Actuator 监控

GC 调优原则参见 垃圾回收 - GC 调优思路


内存泄漏排查

常见原因

原因典型场景
静态集合持续增长static Map 作缓存但不清理
未关闭资源数据库连接、IO 流未 close
内部类持有外部引用匿名类、Lambda 捕获大对象
ThreadLocal 未 remove线程池复用线程时残留
缓存无过期策略本地缓存无限增长
监听器未注销事件监听器、观察者未移除

排查流程

# 1. 观察堆内存趋势(持续上涨不回落 → 疑似泄漏)
#    通过 Actuator 查看
curl http://localhost:8080/actuator/metrics/jvm.memory.used
 
# 2. 触发 Full GC 后查看存活对象
jcmd <pid> GC.run
jmap -histo <pid> | head -30     # 按实例数排序,找异常类
 
# 3. 生成堆快照
jmap -dump:format=b,file=heap.hprof <pid>
# 或配置自动 dump(OOM 时)
# -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heap.hprof
 
# 4. 分析堆快照
#    MAT(Eclipse Memory Analyzer):找 Retained Heap 最大的对象
#    VisualVM:对象引用链追踪
#    JProfiler / Arthas

Arthas 在线诊断

# 启动 Arthas
java -jar arthas-boot.jar <pid>
 
# 查看内存
memory
 
# 查看 GC 信息
gc
 
# 找哪个类实例最多
classloader -a           # 查看类加载器
heapdump /tmp/dump.hprof # 导出堆快照
 
# 追踪方法调用耗时
trace com.example.UserService getUser
watch com.example.UserService getUser returnObj

Spring Boot 应用层调优

连接池

详见 连接池配置

spring:
  datasource:
    hikari:
      maximum-pool-size: 20        # CPU 核数 × 2 + 磁盘数,压测确定
      minimum-idle: 5
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000
      leak-detection-threshold: 60000  # 连接泄漏检测阈值

线程池

详见 异步与线程池

@Bean
public Executor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
    executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors() * 2);
    executor.setQueueCapacity(500);
    executor.setKeepAliveSeconds(60);
    executor.setThreadNamePrefix("app-async-");
    executor.setRejectedExecutionHandler(new CallerRunsPolicy());
    return executor;
}

缓存

详见 缓存Redis 集成

// 避免重复查询数据库
@Cacheable(value = "users", key = "#id", unless = "#result == null")
public User getUser(Long id) {
    return userRepository.findById(id).orElse(null);
}

懒加载

spring:
  main:
    lazy-initialization: true    # 启动时不初始化所有 Bean,按需加载
                                  # 加快启动速度,但首次请求略慢

详见 延迟加载

数据库查询优化

详见 JPA 与 HibernateMyBatis-Plus

// 只查需要的字段,避免 SELECT *
@Query("SELECT new com.example.dto.UserDTO(u.id, u.name) FROM User u WHERE u.id = :id")
Optional<UserDTO> findDtoById(@Param("id") Long id);
 
// 分页查询,避免全表扫描
Page<User> findByDept(String dept, Pageable pageable);
 
// 批量操作,避免 N+1
@BatchSize(size = 100)
@OneToMany(fetch = FetchType.LAZY)
private List<Order> orders;

启动速度优化

# 排查启动慢的 Bean
-Dspring.main.lazy-initialization=true
 
# 分析启动各阶段耗时(Spring Boot 2.4+)
-Dspring.startup.duration.enabled=true
// 记录启动耗时
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        StopWatch sw = new StopWatch();
        sw.start();
        SpringApplication.run(App.class, args);
        sw.stop();
        System.out.println("启动耗时: " + sw.getTotalTimeSeconds() + "s");
    }
}

原生镜像编译(极致启动速度):详见 GraalVM 原生编译


监控指标

通过 Actuator 暴露 JVM 指标,配合 Prometheus + Grafana 监控:

management:
  endpoints:
    web:
      exposure:
        include: health,metrics,prometheus
  metrics:
    export:
      prometheus:
        enabled: true

关键指标:

指标说明
jvm.memory.used各区域内存使用量
jvm.gc.pauseGC 停顿时间
jvm.gc.memory.promoted晋升老年代的数据量
jvm.threads.live活跃线程数
hikaricp.connections.active活跃数据库连接数
http.server.requests请求耗时分布

详见 指标采集链路追踪


JVM 参数完整模板

java \
  # 堆内存
  -Xms2g -Xmx2g \
  # 元空间
  -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \
  # 堆外内存
  -XX:MaxDirectMemorySize=256m \
  # GC
  -XX:+UseG1GC \
  -XX:MaxGCPauseMillis=200 \
  # GC 日志
  -Xlog:gc*:file=logs/gc.log:time,uptime:filecount=5,filesize=20m \
  # OOM 自动 dump
  -XX:+HeapDumpOnOutOfMemoryError \
  -XX:HeapDumpPath=logs/heap.hprof \
  # JIT 优化(详见 JIT.md)
  -XX:+TieredCompilation \
  # 其他
  -XX:+UseStringDeduplication \
  -Dfile.encoding=UTF-8 \
  -Duser.timezone=Asia/Shanghai \
  -jar app.jar

JIT 编译优化详见 JIT,类加载机制详见 类加载