Java 性能调优实战:CPU 飙高、频繁 GC 与内存泄漏排查
本文针对 Java 性能调优中的常见问题进行实战分析,涵盖 CPU 使用率飙高、频繁 GC 及内存泄漏三大场景。通过 top、jstack、jstat、jmap 等工具定位问题根源,提供具体的 JVM 参数调整、代码逻辑优化及缓存策略改进方案。例如解决无限循环导致的 CPU 占用、调整 G1 收集器参数减少 GC 频率、以及使用 Caffeine 替代静态 Map 防止内存泄漏。优化后系统稳定性显著提升,响应时间大幅降低。

本文针对 Java 性能调优中的常见问题进行实战分析,涵盖 CPU 使用率飙高、频繁 GC 及内存泄漏三大场景。通过 top、jstack、jstat、jmap 等工具定位问题根源,提供具体的 JVM 参数调整、代码逻辑优化及缓存策略改进方案。例如解决无限循环导致的 CPU 占用、调整 G1 收集器参数减少 GC 频率、以及使用 Caffeine 替代静态 Map 防止内存泄漏。优化后系统稳定性显著提升,响应时间大幅降低。

本文总结了 Java 开发中常见的性能问题实战案例,涵盖从场景到代码的排查与优化方案。
问题:Java 进程 CPU 使用率突升至 90%+,应用响应缓慢、接口超时
场景:电商秒杀、高频接口调用、实时数据计算等高并发场景
成因:1. 无限循环/高频循环(无阻塞/休眠);2. 频繁 GC(垃圾回收线程占满 CPU);3. 锁竞争激烈导致线程频繁切换;4. NIO 空轮询(JDK 早期 bug)
某电商平台秒杀活动,开抢后 3 分钟接口响应从 200ms 飙升至 2s+,监控显示 Java 进程 CPU 达 95%,部分用户下单失败。
top,找到 CPU 占比最高的 Java 进程(记 PID,如 5678)top -H -p 5678,找到占比最高的线程(记 TID,如 5679)printf "%x\n" 5679,得到结果 163b(jstack 日志用十六进制线程 ID)jstack 5678 > thread_dump.txt,搜索 163b 找到对应线程SeckillService.calculateStock() 方法,排查发现循环内无限制计算库存,陷入高频循环优化前(高频循环):
// 秒杀库存计算方法,无循环限制,高频执行占满 CPU
public void calculateStock(Long seckillId) {
while (true) {
// 库存查询与计算,无阻塞/退出条件
Stock stock = stockMapper.selectById(seckillId);
if (stock.getCount() > 0) {
// 库存扣减逻辑
}
}
}
优化后(加循环限制 + 休眠):
public void calculateStock(Long seckillId) {
int loopCount = 0;
while (true) {
loopCount++;
Stock stock = stockMapper.selectById(seckillId);
if (stock.getCount() > 0) {
// 库存扣减逻辑
break; // 扣减成功退出循环
}
// 循环 5 次无结果则休眠 10ms,减少 CPU 占用
if (loopCount % 5 == 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
// 防止极端情况无限循环,最多循环 100 次退出
if (loopCount > 100) {
log.error("库存计算超时,秒杀 ID:{}", seckillId);
break;
}
}
}
CPU 使用率从 95% 降至 15% 以下,接口响应时间恢复至 200ms 内,秒杀活动无超时、无失败订单,系统稳定性提升 80%。
问题:Young GC 每秒多次,Full GC 分钟级触发,GC 耗时占比超 30%
场景:大数据批量处理、接口频繁创建临时对象、缓存未清理场景
成因:1. 堆内存配置不合理(新生代/老年代过小);2. 大对象频繁创建(直接进入老年代);3. 内存泄漏(对象无法回收,堆内存持续升高)
某金融后台批量对账系统,处理 10 万条账单数据时,每 10 秒触发 1 次 Young GC,每 2 分钟触发 1 次 Full GC,批量任务耗时从 1 小时延长至 3 小时。
jstat -gc 5678 1000 10,查看 GC 次数、耗时(S0C/S1C 为新生代 Survivor 区大小,E 为 Eden 区,O 为老年代)-Xms8g -Xmx8g -XX:NewRatio=2,新生代仅 2.6G,无法满足批量任务需求优化前 JVM 参数:
java -jar 对账系统.jar -Xms8g -Xmx8g -XX:NewRatio=2
优化后 JVM 参数(G1 收集器):
java -jar 对账系统.jar -Xms12g -Xmx12g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=4m -XX:InitiatingHeapOccupancyPercent=35
代码优化(减少大对象重复创建):
// 优化前:循环内创建大对象(每次创建 10KB 的账单对象)
for (String billStr : billList) {
Bill bill = new Bill();
// 大对象赋值逻辑
billService.process(bill);
}
// 优化后:对象复用(使用对象池)
ObjectPool<Bill> billPool = new GenericObjectPool<>(new BillFactory());
for (String billStr : billList) {
Bill bill = billPool.borrowObject();
// 赋值逻辑
billService.process(bill);
billPool.returnObject(bill); // 归还对象复用
}
Young GC 频率降至每 2 分钟 1 次,Full GC 完全消除,GC 耗时占比降至 5% 以下,批量对账任务耗时从 3 小时缩短至 45 分钟,效率提升 75%。
问题:堆内存使用率持续上升不释放,最终触发 OutOfMemoryError
场景:长期运行的后台服务、缓存系统、定时任务系统
成因:1. 静态集合无限制添加元素(如 static List);2. 未关闭资源(数据库连接、文件流);3. 匿名内部类持有外部对象引用;4. 缓存未设置过期时间
某电商用户中心服务,运行 72 小时后内存使用率达 98%,触发 OOM 重启,排查发现购物车缓存未清理,静态 Map 持续存储用户购物车数据。
jmap -dump:format=b,file=heapdump.hprof 5678(问题发生时导出,捕捉真实内存状态)优化前(静态 Map 无清理):
// 静态 Map 存储购物车,无过期、无清理,持续堆积
public class CartCache {
private static Map<Long, UserCart> cartMap = new HashMap<>();
public static void addCart(Long userId, UserCart cart) {
cartMap.put(userId, cart);
}
public static UserCart getCart(Long userId) {
return cartMap.get(userId);
}
}
优化后(使用 Caffeine 缓存,设置过期时间):
// 引入依赖(Maven)
/*
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
</dependency>
*/
// 优化后缓存类
public class CartCache {
// 设置缓存过期时间 30 分钟,最大容量 10 万,自动清理过期数据
private static LoadingCache<Long, UserCart> cartCache = Caffeine.newBuilder()
.expireAfterWrite(30, TimeUnit.MINUTES)
.maximumSize(100000)
.build(userId -> loadCartFromDb(userId));
// 缓存缺失时从 DB 加载
private static UserCart loadCartFromDb(Long userId) {
// 从数据库查询购物车逻辑
return userCartMapper.selectByUserId(userId);
}
public static void updateCart(Long userId, UserCart cart) {
cartCache.put(userId, cart);
}
public static UserCart getCart(Long userId) {
return cartCache.get(userId);
}
}

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online