当前位置:首页 > 技术 > 正文内容

Java 应用性能调优:高 CPU 占用与 Full GC 频繁问题的排查路径

访客 技术 2026年10月30日 1

在生产环境中,Java 应用突然出现响应缓慢、CPU 占用率达到 100% 以及 Full GC(全堆垃圾回收)频繁等问题,是后端工程师经常面临的挑战。这类问题往往伴随着系统吞吐量下降和大量服务报警。本文将深入探讨这些现象背后的逻辑,并提供一套标准化的排查思路与工具链使用方案。

快速止损与信息留存

如果线上系统已经处于不可用状态,首要任务是恢复服务。在重启应用前,必须执行"案发现场"采样,导出 jstack 线程堆栈和内存 Dump 文件。此类问题的核心诱因通常分为两类:

  • 内存异常:代码批量加载了过多数据,导致堆内存耗尽,触发频繁的 Full GC,进而拖慢系统。
  • 计算密集:代码中存在死循环或复杂的算法逻辑,导致 CPU 资源被耗尽。

除了上述直接导致不可用的情况,还有一些隐蔽性问题,如线程阻塞、不当的锁竞争或死锁,它们虽不一定会让 CPU 飙升,但会导致特定的业务接口性能极差。

Full GC 频繁问题的定位

Full GC 频繁通常伴随着 CPU 居高不下,且通过 jstack 观察可以发现大量的垃圾回收线程(如 VM Thread)处于活跃状态。通过 jstat 监控可以看到 FGC 计数器在短时间内快速增长。

排查步骤

  1. 使用 top 命令锁定高负载进程。
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
    1234 root      20  0  3.2g    1.5g    18m  S  99.2 18.5   5:12.40 java
  2. 查看该进程下的线程详情:top -Hp 1234。
    PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
    1235 root      20   0 3.2g    1.5g    18m  R 85.0 18.5   4:20.12 java
    1236 root      20   0 3.2g    1.5g    18m  S 10.5 18.5   0:45.30 java
  3. 将线程 PID(1235)转换为十六进制:printf "%x\n" 1235 得到 4d3。
  4. 在 jstack 输出中检索 0x4d3,如果看到 VM Thread,说明 CPU 正被垃圾回收占用。

深入分析

若确认是 GC 问题,需观察内存增长情况:jstat -gcutil 1234 1000 5。若 FGC 频繁且回收效果不佳,应导出堆快照:

jmap -dump:format=b,file=heap_dump.hprof 1234

使用 MAT (Memory Analyzer Tool) 分析快照。下表展示了可能的分析结果:

MAT Object Tree

除了对象激增,还需警惕 System.gc() 的显式调用。如果 Dump 文件显示 GC 原因(Cause)为 System.gc(),可通过 JVM 参数 -XX:+DisableExplicitGC 来屏蔽此类调用。

计算资源过度消耗

当 jstack 定位的线程并非 GC 线程,而是业务逻辑线程(如 http-nio-exec)时,说明存在计算密集型任务。通过堆栈信息可以精确到代码行号。

CPU Intensive Stack Trace

在此场景下,开发者应检查对应的业务逻辑,例如是否存在大列表的循环嵌套、未经优化的正则匹配或复杂的加解密操作。

偶发性接口响应缓慢

这类问题最具挑战性,因为现象是随机的。此时 CPU 和内存往往看起来很正常。排查思路是:人为提高复现频率。

通过性能压测工具(如 JMeter)对该接口持续施压。在接口响应变慢时,多次抓取 jstack 堆栈。如果大量线程同时停留在某一行代码(如 Thread.sleep、数据库连接等待或外部接口调用),该行代码即为瓶颈点。

"exec-7" #45 daemon prio=5 tid=0x00007f... nid=0x1a2b waiting on condition
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at com.tech.service.impl.OrderServiceImpl.checkStatus(OrderServiceImpl.java:54)

线程长时期处于 WAITING 状态

这种情况常见于分布式锁未释放、第三方客户端响应超时或 CountDownLatch 计数未清零。线程可能进入了永久等待状态。

排查方法是时序对比法:

  1. 间隔 10 秒导出两个 jstack 文件。
  2. 筛选出状态为 WAITING 或 TIMED_WAITING 的非框架线程(即自定义业务线程)。
  3. 对比两个文件,如果某个线程的 nid 和堆栈位置在两次采样中完全一致,说明该线程可能已经僵死。

死锁问题的快速识别

死锁是最容易定位的问题之一。jstack 在生成报告时会自动进行死锁检测。如果存在死锁,报告末尾会明确列出 Found one Java-level deadlock 以及涉及的线程和锁地址。

Deadlock Detection

开发者只需根据报告提供的锁持有关系(Waiting to lock / Locked),调整业务代码中的加锁顺序,保证锁获取的原子性或有序性即可解决。

排查逻辑汇总

处理线上性能问题应遵循先宏观后微观的逻辑:

  • Top 观察:区分是 CPU 问题还是 IO/阻塞问题。
  • 线程分析:区分是 VM 线程(GC 问题)还是业务线程(代码问题)。
  • 内存 Dump:解决 FGC 和内存泄漏的唯一利器。
  • 堆栈采样:通过对比多份 jstack 解决线程卡死与接口缓慢。

标签: JVM
返回列表

上一篇:Linux 终端命令历史管理与彻底清除指南

没有最新的文章了...

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。