如何快速定位并优化CPU 与 JVM 内存性能瓶颈?

简介: 如何快速定位并优化CPU 与 JVM 内存性能瓶颈?

双十一大促前夕,部门组织了核心应用全链路压测,你负责的订单中心在第一波压测流量脉冲下 CPU 利用率瞬间飙升到 95% 以上,接口调用大量超时,成为全链路卡点,最终导致压测活动草草结束,主管责令限期1天解决,该如何快速定位 CPU 性能瓶颈完成优化?



熬夜爆肝写了2千行代码,终于赶在项目截止日期前完成线上发布,没等你美美的喝完一瓶冰可乐,手机就开始滴滴的响个不停,告警电话如雨后春笋般接踵而至,JVM 内存持续 FGC,请求超时流量下跌,面对领导和客户的催促,该如何快速定位内存性能瓶颈解决风险?



以上场景对于参与 Java 应用研发或运维的同学来说,相信都不陌生。CPU 和 JVM 内存是 Java 应用的核心资源,一旦出现热点导致资源不足,很容易引发大面积故障。因此,掌握高效的 CPU 与 JVM 内存性能优化手段就显得尤为重要。


CPU 性能优化实战

Cloud Native


CPU(Central Processing Unit)是计算机系统的运算和控制核心,是信息处理、程序运行的最终执行单元,相当于系统的“大脑”。当 CPU 过于繁忙,就像“人脑”并发处理过多的事情,会降低做事的效率,严重时甚至会导致崩溃“宕机”。因此,合理控制 CPU 的负载,是保障系统稳定持续运行的重要手段。


CPU 使用率是 CPU 非空闲态运行的时间占比,它反映了 CPU 的繁忙程度。比如,单核 CPU 1s 内非空闲态运行时间为 0.8s,那么它的 CPU 使用率就是 80%;双核 CPU 1s 内非空闲态运行时间分别为 0.4s 和 0.6s,那么,总体 CPU 使用率就是 (0.4s + 0.6s) / (1s * 2) = 50%,其中 2 表示 CPU 核数,多核 CPU 同理。根据经验法则, 建议生产系统的 CPU 总使用率不要超过 70%。



CPU 使用率只反映系统健康状态的度量指标,并不是问题的根因。因此,它的价值主要体现在两个方面: 一是综合反映当前系统的健康程度,结合监控告警产品,实现快速响应;二是初步定界问题方向,缩小排查范围,降低故障恢复时间。 比如当 CPU iowait 高时,应优先排查磁盘 I/O;当 CPU steal 高时,就优先排查宿主机状态。CPU 涵盖的问题场景有很多,限于篇幅限制,下面以最常见的用户态 CPU 使用率高为例,介绍下 Java 应用的排查思路。


如何排查用户态 CPU 使用率高?


用户态 CPU 使用率反映了应用程序的繁忙程度,通常与我们自己写的代码息息相关。因此,当你在做应用发布、配置变更或性能优化时,如果想定位消耗 CPU 最多的 Java 代码,可以遵循如下思路:

  • 通过 top 命令找到 CPU 消耗最多的进程号;
  • 通过 top -Hp 进程号 命令找到 CPU 消耗最多的线程号(列名仍然为 PID);
  • 通过 printf "%x\n" 线程号 命令输出该线程号对应的 16 进制数字;
  • 通过 jstack 进程号 | grep 16进制线程号 -A 10 命令找到 CPU 消耗最多的线程方法堆栈。






上述方法是目前业界常用的诊断流程,然而该方法有两个显著缺陷,一是操作流程复杂,而且往往一次 jstack 还不足以定位根因,需要执行多次;二是只能用于诊断在线问题,无法记录历史快照,如果问题已经发生,无法复现的话,往往只能不了了之。



为了解决上述问题,业界领先的 APM 产品已经支持了常态化记录线程/方法栈 CPU开销的持续剖析能力,随时回溯历史快照,对比不同时段的 CPU 热点变化。以阿里云 ARMS 产品为例,典型的 CPU 热点排查思路主要分为以下几步:


1.通过主机/Pod CPU 利用率监控或告警,第一时间发现 CPU 利用率异常飙升现象。




2.通过线程分析监控,快速找到 CPU 消耗最高的线程池,比如 Pressure-CPU*。




3.通过持续剖析-CPU热点功能,回溯任意时间段内的 CPU 占比火焰图,直接定位到性能瓶颈方法,比如下图中 CPUPressure.runBusiness( ) 方法的 CPU 开销占比高达 99.7%,研发同学定位到具体的业务代码行,就可以快速优化代码解决 CPU 热点问题。




4.生产系统的方法调用栈更加复杂,ARMS 还支持差分火焰图直观对比不同时间段的 CPU 开销变化,比如应用发布、大促压测等场景,再结合 Copilot 智能诊断给出影响 CPU 变化的关键方法,无需丰富的专家经验也能轻松完成性能优化工作,如下图所示。





JVM 内存性能优化实战

Cloud Native


内存(Memory),作为计算机系统中的关键组成部分,不仅影响着程序运行的速度,还决定了多任务处理的能力以及数据访问的效率。从本质上讲,内存是一种临时存储介质,用于存放正在执行的程序及其相关数据,以便CPU能够快速访问。相比于硬盘等长期存储设备,内存具有更高的读写速度,但其容量相对较小且断电后信息会丢失。因此,在计算机体系结构中,合理配置与优化使用内存资源显得尤为重要。


JVM 的内存主要分为堆(Heap)、栈(Stack)、方法区(Method Area)等几个部分。其中,堆用于存放对象实例,而栈则存储了方法调用过程中产生的局部变量及操作数栈等信息。方法区主要用于保存类结构信息如运行时常量池等。其中,堆区域是最容易产生内存热点的地方,因为它直接关联着对象生命周期管理和垃圾收集活动。当 JVM 内存严重不足时,就会抛出 java.lang.OutOfMemoryError 错误,常见的 OOM 类型如下图所示。






JVM 内存热点成因分析


常见的 JVM 内存热点产生原因主要包括以下几类,每种原因背后都隐藏着复杂的机制。

1.对象创建过于频繁:如果存在大量短生命周期的对象被频繁地创建与销毁,这将导致垃圾回收器(Garbage Collector, GC)频繁工作以清理不再使用的对象空间。这种情况下,即使GC算法本身效率很高,但由于其执行频率过高,仍然会对系统性能造成显著影响。例如,在循环体内部创建临时变量而不进行复用。为了缓解这一问题,可以考虑使用对象池技术或尽量减少不必要的对象实例化操作。还有一种情况是上游系统请求流量飙升,常见于各类促销/秒杀活动,此时可以考虑添加机器资源,或者做限流降级。


2.大对象分配:当应用程序中申请大对象时(如大型数组),通常会被直接分配到老年代而非新生代区域。虽然这样做可以避免短期内因这些大对象而触发 YoungGC,但如果此类对象数量较多,则可能会迅速填满老年代空间,进而迫使Full GC发生。Full GC会暂停所有用户线程并扫描整个堆区,因此对应用性能的影响尤为严重。针对这种情况,建议评估是否真的需要如此大的数据结构,并探索更高效的数据表示方式。


3.内存泄漏:尽管Java具有自动内存管理功能,但不当的设计模式或编程习惯仍可能导致内存泄露问题。比如,静态集合类持有外部引用、未关闭的数据库连接等都是常见场景。随着时间推移,这些无法被正常回收的对象逐渐积累起来,最终耗尽可用堆空间。解决之道,首先通过一些监控分析工具定界不断增长的内存位置来源,判断内存泄露是发生在堆内还是堆外,如果是堆内可以借助诸如jmap等工具下载内存快照,检查堆内占比高的内存对象,并结合代码分析根因。如果是堆外部分出现了内存稳定增长,此时需要借助一些外部诊断工具,比如 NMT(Native Memory Tracking)等对堆外内存申请情况进行监测,分析可能的原因。


4.不合理的堆大小设置:JVM启动参数中的-Xms(初始堆大小)和-Xmx(最大堆大小)对于控制内存使用至关重要。如果这两个值设置得过低,则可能因为频繁的GC活动而降低程序性能;反之,若设定得过高,则又会浪费宝贵的物理内存资源。理想状态下,应根据实际业务需求及硬件配置情况合理调整这两个参数,一般设置为总内存大小的1/2左右,然后留1/2给非堆部分使用。此外,-XX:NewRatio等选项的设置也很重要,需要基于其去平衡新生代与老年代的比例关系,从而达到最佳性能状态。


5.加载的 class 数目太多或体积太大:永久代(Permanent Generation,JDK 1.8 使用 Metaspace 替换)的使用量与加载到内存的 class 的数量/大小正相关。当加载的 class 数目太多或体积太大时,会导致 永久代用满,从而导致内存溢出报错。可以通过 -XX:MaxMetaspaceSize / -XX:MaxPermSize 上调永久代大小。


如何排查 JVM 内存热点问题?


生产环境需要常态化跟踪 JVM 内存变化,如何第一时间发现 JVM 内存问题,并快速定位止血,整体思路与 CPU 热点优化类似,主要包括以下步骤:


1.通过 JVM 监控/告警发现内存或 GC 异常,分析新生代、老年代、Metaspace、DirectBuffer 等内存变化。




2.通过持续剖析-内存热点功能,常态化记录每个方法的内存对象分配占比火焰图,比如下图中AllocMemoryAction.runBusiness() 方法消耗了 99.92% 的内存对象分配。


 


3.内存快照记录了相关时刻的堆内存对象占用和进程类加载等信息。阿里云 ARMS 提供了一种开箱即用的内存快照白屏化操作功能,让快照创建、获取和分析更加简单便捷。结合阿里云 ATP 分析工具,实现了 JVM 内存对象与引用关系的深入分析和诊断。




小结

Cloud Native



本文介绍了 Java 应用常见的 CPU & JVM 内存热点原因及优化思路,首先通过监控告警及时发现资源使用率的异动,然后结合方法级别的 CPU/内存火焰图定位热点代码,帮忙研发同学快速排障,优化系统资源使用,确保应用在高负载下的稳定运行。

相关文章
|
8天前
|
监控 Java Unix
6个Java 工具,轻松分析定位 JVM 问题 !
本文介绍了如何使用 JDK 自带工具查看和分析 JVM 的运行情况。通过编写一段测试代码(启动 10 个死循环线程,分配大量内存),结合常用工具如 `jps`、`jinfo`、`jstat`、`jstack`、`jvisualvm` 和 `jcmd` 等,详细展示了 JVM 参数配置、内存使用、线程状态及 GC 情况的监控方法。同时指出了一些常见问题,例如参数设置错误导致的内存异常,并通过实例说明了如何排查和解决。最后附上了官方文档链接,方便进一步学习。
|
1月前
|
存储 监控 Java
JVM实战—8.如何分析jstat统计来定位GC
本文详细介绍了使用jstat、jmap和jhat等工具分析JVM运行状况的方法,以及如何合理优化JVM性能。内容涵盖新生代与老年代对象增长速率、Young GC和Full GC的触发频率及耗时等关键指标的分析。通过模拟BI系统和计算系统的案例,展示了如何根据实际场景调整JVM参数以减少FGC频率,提升系统性能。最后汇总了常见问题及其解决方案,帮助开发者更好地理解和优化JVM运行状态。
JVM实战—8.如何分析jstat统计来定位GC
|
1月前
|
缓存 监控 算法
JVM实战—10.MAT的使用和JVM优化总结
本文详细探讨了JVM内存管理与性能优化的关键问题。首先分析了线上大促活动引发的老年代内存泄漏及频繁FGC问题,通过MAT工具定位到本地缓存未正确处理的原因,并提出使用Ehcache等框架解决。接着讨论了百万级数据误处理导致的频繁FGC案例,深入剖析String.split()方法在特定JDK版本下的内存消耗问题,并给出多线程并发处理大数据量的优化建议。文章还总结了JVM运行原理、GC机制以及YGC和FGC的触发条件,明确了正常系统GC频率指标。最后提供了JVM性能优化的整体思路,包括新系统开发时的参数预估、压测后的调整策略以及线上系统的监控方法,同时列举了常见的FGC原因及对应解决方案。
176 79
JVM实战—10.MAT的使用和JVM优化总结
|
1天前
|
Arthas 监控 Java
Arthas memory(查看 JVM 内存信息)
Arthas memory(查看 JVM 内存信息)
27 6
|
26天前
|
监控 C#
【Function App】如果一个拥有多个Function App的Plan遇见了High CPU问题? 如何方便定位是哪一个Function App引发的呢?
在Azure Function App测试中,若多个Function App共用同一App Service Plan资源,当出现High CPU问题时,由于Function App公开指标无法直接观测CPU状态,可通过启用Application Insights解决。其Live Metrics功能可过滤并查看每个Function App的CPU使用情况。具体步骤为:将所有Function App连接至同一Application Insights资源,进入Live Metrics页面按Role筛选监控数据。附有三段C#代码示例,分别展示占用CPU、Memory及普通功能的实现方法。
84 36
|
1月前
|
存储 缓存 算法
JVM简介—1.Java内存区域
本文详细介绍了Java虚拟机运行时数据区的各个方面,包括其定义、类型(如程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和直接内存)及其作用。文中还探讨了各版本内存区域的变化、直接内存的使用、从线程角度分析Java内存区域、堆与栈的区别、对象创建步骤、对象内存布局及访问定位,并通过实例说明了常见内存溢出问题的原因和表现形式。这些内容帮助开发者深入理解Java内存管理机制,优化应用程序性能并解决潜在的内存问题。
179 29
JVM简介—1.Java内存区域
|
1月前
|
消息中间件 Java 应用服务中间件
JVM实战—2.JVM内存设置与对象分配流转
本文详细介绍了JVM内存管理的相关知识,包括:JVM内存划分原理、对象分配与流转、线上系统JVM内存设置、JVM参数优化、问题汇总。
JVM实战—2.JVM内存设置与对象分配流转
|
1月前
|
缓存 监控 Java
JVM实战—12.OOM的定位和解决
本文详细探讨了JVM内存管理中的常见问题及其解决方案,包括如何监控和报警系统的OOM异常、在内存溢出时自动Dump内存快照、解决Metaspace区域内存溢出、栈内存溢出(StackOverflowError)以及堆内存溢出(OutOfMemoryError: Java heap space)。针对每种情况,文章提供了具体的解决思路、示例代码、GC日志分析及内存快照分析方法。通过搭建系统监控体系、调整JVM参数和使用工具如MAT,可以有效定位和解决各类内存问题,优化系统性能并避免崩溃风险。
JVM实战—12.OOM的定位和解决
|
1月前
|
缓存 监控 算法
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略
|
1月前
|
监控 Java 编译器
聊聊JVM如何优化
JVM的优化是一个复杂而细致的过程,涉及内存管理、垃圾回收、即时编译、线程调度等多个方面。通过合理配置JVM参数、选择合适的垃圾回收器、优化线程调度和使用专业的监控工具,可以大幅提升Java应用的性能和稳定性。掌握这些优化技巧,能够帮助开发者在高并发、高负载的生产环境中保持系统的高效运行。
121 13

热门文章

最新文章

相关实验场景

更多
下一篇
oss创建bucket