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