java 服务器cpu内存过高 如何排查和解决

一、 快速诊断与初步定位

当告警响起时,首先要快速判断是 CPU 问题 还是 内存问题,或者是两者兼有。

1. 使用操作系统命令快速查看

a) 整体资源查看 - top 命令

bash

top

看 CPU:查看 %CPU 列,找到占用最高的进程ID(PID)。

看内存:查看 %MEM 列 和 RES 列,找到消耗内存最多的进程。

关键指标:load average 如果持续高于 CPU 核心数,说明系统负载过高。

b) 更精细的进程查看 - htop (如果已安装)

bash

htop

它比 top 更直观,支持颜色和鼠标操作。

c) 内存专用 - free -h

bash

free-h

查看内存和 Swap 的使用情况。如果 Swap 被频繁使用,说明物理内存不足,性能会急剧下降。

二、 深入排查:针对 Java 进程

找到占用资源高的 Java 进程 PID 后,进行深入分析。

1. CPU 过高排查

a) 找到消耗 CPU 最高的 Java 线程

使用 top -H -p <pid> 查看该 Java 进程下的所有线程。

记下 CPU 占用最高的线程 ID(例如 12345),并将其转换为十六进制(Java 线程栈中显示的是十六进制)。

bash

printf"%x\n"12345# 输出:3039

b) 获取线程转储,分析线程在做什么

使用 jstack 命令获取线程快照:

bash

jstack<pid>>jstack.log

然后在这个 jstack.log 文件中,搜索刚才转换的十六进制线程 ID 0x3039。找到对应的线程栈,就能看到这个线程正在执行什么方法、哪一行代码。通常这里就能直接定位到问题代码

常见原因:

无限循环或密集型计算:线程栈显示在某个循环或计算逻辑中。

锁竞争激烈:大量线程处于 BLOCKED 状态,等待同一个锁。

GC 频繁:如果大量线程是 GC 线程,则问题根源在内存。

c) 使用 Arthas 在线诊断(强烈推荐)

Arthas 是阿里开源的 Java 诊断神器,无需预装,直接 attach 到线上进程进行诊断。

快速定位热点方法

bash

# 启动 arthasjava-jararthas-boot.jar<pid># 使用 profiler 生成火焰图,直观展示 CPU 热点profiler start# ...等待几秒...profiler stop--formathtml

火焰图可以非常清晰地看到 CPU 时间都花在了哪些方法调用上。

监控方法执行时间

bash

# 监视特定方法的调用耗时watchcom.example.YourClass yourMethod'{params, returnObj, throwExp}'-n5-x3

2. 内存过高排查

内存过高通常意味着 内存泄漏 或 对象数量/体积超出预期

a) 查看 JVM 内存概况

使用 jstat 命令观察 GC 情况:

bash

jstat-gcutil<pid>100010# 每1秒打印一次,共10次

关注:

O (Old Space) 老年代使用率:如果一直很高且只升不降,说明很可能有内存泄漏。

FGC / FGCT:Full GC 的次数和时间。如果 FGC 很频繁且 FGCT 很长,说明 GC 在拼命工作但效果不佳,是内存问题的典型症状。

b) 生成并分析堆转储文件

这是定位内存泄漏最有效的方法。

生成堆转储

bash

# 使用 jmap 命令 (会影响性能,谨慎在高峰使用)jmap -dump:live,format=b,file=heap_dump.hprof<pid># 或者,在 JVM 启动参数中添加,当发生 OOM 时自动生成-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/path/to/dump.hprof

分析堆转储

使用专业工具分析 heap_dump.hprof 文件:

Eclipse MAT:功能强大,可以自动分析泄漏嫌疑。

JVisualVM:JDK 自带,基础分析足够。

JProfiler:商业软件,非常强大。

分析技巧

在 MAT 中,查看 Histogram,按对象数量或 Shallow Heap 排序,找到占用最大的对象类型。

查看 Dominator Tree,找到内存中占据主导地位的对象(即持有大量内存引用的对象)。

查看 Leak Suspects Report(MAT 自动生成),它经常能直接给出泄漏的线索。

常见原因:

内存泄漏:静态集合类(如 Map, List)持续添加对象从未清除;未关闭的连接(数据库、网络、文件);监听器未注销。

大对象/缓存:加载了大文件到内存,或者缓存(如 Guava Cache, Ehcache)没有大小限制或过期策略。

不合理的 JVM 参数:堆内存设置过小,导致频繁 GC,或者设置过大,导致一次 Full GC 停顿时间过长。

三、 解决方案

根据排查出的根本原因,采取相应措施。

1. 针对 CPU 问题

优化算法:如果找到热点方法,优化其逻辑,减少循环次数或使用更高效的算法。

减少锁竞争

缩小同步代码块的范围。

使用并发包下的无锁类(如 ConcurrentHashMap)。

使用读写锁(ReadWriteLock)代替同步锁。

异步化:将耗时操作(如IO、远程调用)异步化,避免阻塞工作线程。

限流和降级:对于突增的流量,在入口层进行限流,防止服务器被压垮。

2. 针对内存问题

修复内存泄漏:根据堆转储分析结果,修复代码。例如:

对于静态集合,使用弱引用(WeakHashMap)或确保有移除机制。

确保使用 try-with-resources 语句关闭所有连接。

合理使用缓存

为缓存设置合理的最大容量和过期时间。

考虑使用分布式缓存(如 Redis)来分担单机内存压力。

优化对象创建:避免在循环中创建大量对象,重用对象(使用对象池需谨慎)。

调整 JVM 参数

-Xmx 和 -Xms:设置堆的初始和最大大小,通常设置为相同值,避免动态调整带来的开销。

选择合适的 GC 器。对于高吞吐量应用,可用 -XX:+UseG1GC(G1 垃圾回收器);对于低延迟要求,可考虑 ZGC 或 Shenandoah。

四、 长期预防与监控

完善的监控系统:集成 APM 工具(如 SkyWalking, Pinpoint, Prometheus + Grafana),对 JVM(GC 次数、时间、内存使用率)、应用 QPS、响应时间等进行实时监控和告警。

性能测试:在上线前进行充分的压力测试,了解应用的瓶颈和最大承载能力。

代码审查:在代码层面避免常见的性能陷阱,如大对象、N+1 查询等。

定期健康检查:定期对生产系统进行 Arthas 诊断或生成堆转储,主动发现潜在问题。

总结排查流程图

text

告警触发

    ↓

top/free 快速定位 (CPU? 内存?)

    ↓

若是 CPU 高:                  若是内存高:

    top -H -p <pid>                jstat -gcutil <pid>

    printf "%x" <nid>                jmap -dump ...

    jstack <pid> | grep <hex-nid>    MAT 分析堆转储

    或使用 Arthas profiler

    ↓

分析线程栈/火焰图/堆转储,定位问题代码

    ↓

根据根本原因进行修复(优化代码/调整配置)

    ↓

验证、上线、持续监控

通过这套系统性的方法,绝大多数 Java 服务器的 CPU/内存问题都可以被有效地定位和解决。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容