内存溢出+CPU占用过高:问题排查+解决方案+复盘(超详细分析教程)

简介: 全网最全的内存溢出CPU占用过高排查文章,包含:问题出现现象+临时解决方案+复现问题+定位问题发生原因+优化代码+优化后进行压测,上线+复盘

前言


最近刚上线了一款社交项目,运行十多天后(运营持续每天推量),发现问题:

  • 系统OOM(资源不能被释放)导致服务器频繁且长时间FGC导致服务器CPU持续飚高
  • 日志中内存溢出:**java.lang.OutOfMemoryError: Java heap space**
  • 程序十分卡顿,严重影响用户使用


从以下方面,为大家分享此次问题解决流程

  • 问题出现现象
  • 临时解决方案
  • 复现问题
  • 定位问题发生原因
  • 优化代码
  • 优化后进行压测,上线
  • 复盘

学完本博文,你的收获

  • 排查内存溢出的思路
  • 排查内存溢出过程中用到的命令及工具(Linux命令,[Eclipse Memory Anaylzer[MAT]](https://www.eclipse.org/mat/))
  • 定位系统内存溢出的代码,并进行优化
  • 此次内存溢出问题复盘

解决方案流程图

111.png

问题&临时解决方案&定位问题&最终解决方案


问题:


  • 业务反馈程序用的十分卡,同时测试自己测的也十分卡
  • 从ELK收集的请求日志发现确实存在问题,线上是两台部署:两台机器上都是,一次请求耗时由原来的几毫秒变为10几秒
  • CPU跑的过高,当时是4核,CPU持续飙到350%+;
  • 当时一台服务器CPU截图:

222.png

临时解决方案

  • 当时为了减少对业务影响,直接将生产两台服务器上的项目进行重启
  • 项目启动参数中没有加内存溢出日志输出(后续博客为大家介绍JVM调优时讲解启动命令中加内存溢出日志输出),重启后出问题时项目的JVM信息丢失了


复现问题方式:在开发环境对程序进行持续压测;压测相关服务器配置:

  • 服务器配置:8核,16G
  • 项目启动内存:136M
  • Jmeter持续(循环)压发消息接口10分钟


定位问题

  • top命令查看最耗CPU的进程(进程:17038;CPU持续飙到595%+)
# 输入top命令后键入P(大写P),进程按照CPU从高到底排序top

333.png


  • 查看该进程中最耗CPU的线程(发现有一些线程占用CPU较高)    
# 17038为进程号,键入P(大写P),该进程中的线程按照CPU从高到底排序top-Hp17038

555.png

 

  • 将线程号转为16进制,同时查看这些线程当前正在干什么(在此以17045线程为例)
# 将线程号转为16进制;其中17045为线程号printf '%x\n'17045# 17038为进程号,0x4295为最耗CPU线程的十六进制jstack 17038 | grep'0x4295'-C10--color

xxx.png

  • 可以看到**最耗CPU的线程都是在进行GC**
  • 用Jmap命令查看当前堆的使用情况(发现老年代现在已占用99.8%+)  
# 其中17038为进程号jmap -heap17038

yyy.png

  • 查看gc频率的命令(其中O代表老年代占用率,FGC是FullGC次数,FGCT是fullGC时间;可以看出在频繁FullGC但是老年代有资源一直释放不掉)
# 其中17038为进程号,5000是指每5秒(5000毫秒)输出一次jstat -gcutil170385000

av.png

  • 通过分析出问题时线上日志发现内存溢出;至此定位到问题根源是内存溢出导致(有未释放资源堆积,导致老年代被占满,然后频繁的FullGC但是资源一直释放不了)  
grep-m10'OutOfMemoryError' *.log

123.png

分析问题产生原因

  • 由于线上当时直接重启,未能保留当时的JVM内存文件;在开发环境进行循环压测,复现线上问题,然后导出dump文件进行分析找到原因
  • 生成dump文件命令
# 其中fileName是导出后dump名称,pid为进程号jmap -dump:format=b,file=fileName.dump pid
  • 将dump文件导出到本地,用Eclipse Memary Analysis(MAT官网) 进行分析

 

  • MAT导入dump文件
  • q.png

 w.png

  • 按对象排序视图进行查看(总览中看到对象总个数:14.1百万个)

e.png

 

  • 发现有两个类(ClassClassPath,ClassClassPathList)占用比较大,这两个类约占对象总数的**83%**(计算方式:5873361*2/14100000=83%)    

r.png

分析代码      

  • 去代码中全局搜这两个类,发现只有在打日志的时候用到ClassClassPath类

o.png

  • 分析ClassClassPath相关代码:
  • 用到ClassClassPath对象是一个静态的ClassPool;
  • 问题原因:classPath一直被静态的全局pool所持有,导致GC一直释放不掉;

s1.png

s2.png

  s3.png

s4.png

  • 当然顺着代码,顺藤摸瓜也找到了ClassPathList

s5.png

s6.png优化代码:每次用完ClassClassPath后将其释放

  • 每次对象使用完后从静态pool中移除
  • 注意:classPath=null这种方式是不能释放掉的

xc.png

优化后再次进行验证


  • 开发环境循环压测,用MAT分析dump文件,发现内存中已不再堆积ClassClassPath类;优化前后接口吞吐量也提升8.2%
  • 进行线上发布,观察一周后,对内存分析发现正常

复盘:


  • 项目比对:
  • 为快速开发,社交的代码从原来金融项目基础上改造而来;
  • 原来金融项目没有内存溢出,而社交项目为什么内存溢出?
  • 通过ELK统计一段时间的访问量结果:
  • 社交目前日访问后台量65w+
  • 金融项目只有4.5W+
  • 社交和金融项目业务类型不一样,所呈现出的特点也不同
  • 去生产的金融项目中dump内存文件,用MAT工具分析,发现也存在ClassClassPath类堆积释放不掉,只不过由于访问量少,堆积量未占满老年代而已;果断在金融项目迭代时将其优化;
  • 程序预警:为减少业务影响,增加接口耗时的预警(后续博文为大家共享);实现方式:
  • 在每次程序处理完进行预警(比如本次请求>阈值);缺点:消耗性能影响正常业务
  • 在ELK清洗时用相关插件进行预警;优点:和业务解耦,对业务无影响
  • 服务器预警:运维增加CPU内存,日志内存溢出监控



总结


解决内存溢出过程总结:


  • 不同的项目导致内存溢出原因是不同的;
  •    重要的是排查思路
  •    经过不断的耐心的去观察,测试,分析才能定位到问题并最终解决问题


  • 在这次分析内存溢出过程中,我们也针对我们项目的JVM启动参数进行了调优,在接下来的博文中为大家分享JVM调优
相关文章
|
3月前
|
存储 弹性计算 缓存
阿里云服务器ECS经济型、通用算力、计算型、通用和内存型选购指南及使用场景分析
本文详细解析阿里云ECS服务器的经济型、通用算力型、计算型、通用型和内存型实例的区别及适用场景,涵盖性能特点、配置比例与实际应用,助你根据业务需求精准选型,提升资源利用率并降低成本。
255 3
|
2月前
|
存储
阿里云轻量应用服务器收费标准价格表:200Mbps带宽、CPU内存及存储配置详解
阿里云香港轻量应用服务器,200Mbps带宽,免备案,支持多IP及国际线路,月租25元起,年付享8.5折优惠,适用于网站、应用等多种场景。
659 0
|
30天前
|
弹性计算 前端开发 NoSQL
2025最新阿里云服务器配置选择攻略:CPU、内存、带宽与系统盘全解析
本文详解2025年阿里云服务器ECS配置选择策略,涵盖CPU、内存、带宽与系统盘推荐,助你根据业务需求精准选型,提升性能与性价比。
|
2月前
|
存储 人工智能 自然语言处理
AI代理内存消耗过大?9种优化策略对比分析
在AI代理系统中,多代理协作虽能提升整体准确性,但真正决定性能的关键因素之一是**内存管理**。随着对话深度和长度的增加,内存消耗呈指数级增长,主要源于历史上下文、工具调用记录、数据库查询结果等组件的持续积累。本文深入探讨了从基础到高级的九种内存优化技术,涵盖顺序存储、滑动窗口、摘要型内存、基于检索的系统、内存增强变换器、分层优化、图形化记忆网络、压缩整合策略以及类操作系统内存管理。通过统一框架下的代码实现与性能评估,分析了每种技术的适用场景与局限性,为构建高效、可扩展的AI代理系统提供了系统性的优化路径和技术参考。
147 4
AI代理内存消耗过大?9种优化策略对比分析
|
2月前
|
存储 弹性计算 固态存储
阿里云服务器配置费用整理,支持一万人CPU内存、公网带宽和存储IO性能全解析
要支撑1万人在线流量,需选择阿里云企业级ECS服务器,如通用型g系列、高主频型hf系列或通用算力型u1实例,配置如16核64G及以上,搭配高带宽与SSD/ESSD云盘,费用约数千元每月。
194 0
|
8月前
|
存储 设计模式 监控
快速定位并优化CPU 与 JVM 内存性能瓶颈
本文介绍了 Java 应用常见的 CPU & JVM 内存热点原因及优化思路。
899 166
|
6月前
|
存储 监控 算法
taosd 写入与查询场景下压缩解压及加密解密的 CPU 占用分析
在当今大数据时代,时序数据库的应用越来越广泛,尤其是在物联网、工业监控、金融分析等领域。TDengine 作为一款高性能的时序数据库,凭借独特的存储架构和高效的压缩算法,在存储和查询效率上表现出色。然而,随着数据规模的不断增长,在保证数据安全性和存储效率的同时,如何优化 CPU 的资源占用,成为了一个值得深入讨论的问题。
117 1
|
6月前
|
存储 Java
课时4:对象内存分析
接下来对对象实例化操作展开初步分析。在整个课程学习中,对象使用环节往往是最棘手的问题所在。
|
6月前
|
Java 编译器 Go
go的内存逃逸分析
内存逃逸分析是Go编译器在编译期间根据变量的类型和作用域,确定变量分配在堆上还是栈上的过程。如果变量需要分配在堆上,则称作内存逃逸。Go语言有自动内存管理(GC),开发者无需手动释放内存,但编译器需准确分配内存以优化性能。常见的内存逃逸场景包括返回局部变量的指针、使用`interface{}`动态类型、栈空间不足和闭包等。内存逃逸会影响性能,因为操作堆比栈慢,且增加GC压力。合理使用内存逃逸分析工具(如`-gcflags=-m`)有助于编写高效代码。
128 2