好难~记录一次生产上的OOM解决过程
csdh11 2025-03-10 14:36 14 浏览
点击上方,轻松关注!及时获取有趣有料的技术文章
记录一次生产上的OOM解决过程
一.项目架构
SpringCloud Dalston.SR1 + SpringBoot 1.5.9 + Mysql +Redis + RabbitMQ
所有的业务模块的应用服务都部署在同一个服务器,且单实例部署,服务器配置4核32G。
二. 原因分析
自己所负责的data模块这两天OOM较多,导致服务重启;
data服务主要业务是报表相关,数仓对接的业务以及多个外部数据相关的小程序的后台,与数据库的交互比较多,业务逻辑相对其他模块较为简单,
第一次:2月25日OOM情况:
由于Redis反序列化失败导致的OOM
第二次:2月26日的OOM情况:
由于GC无法回收对象导致
第一次发生OOM时,觉得可能就是由于Redis序列化器和反序列化器不一致,原有的JVM参数仅设置时-Xmx:512m -Xms:512m, 老年代:年轻代=2:1 ,老年代大概分配有300M内存
时候排查问题时,发现Redis的使用都是用自己用RedisTemplate封装的工具类,按道理说不会出现什么问题,并未过多关注;
第二次发生OOM时,与第一次相距的时间仅为1天,当时就觉得问题不对了,
1.首先使用jmap -histo:live pid 查看 服务内存活的对象,发现 [C 类型的数组和ConcurrentHashMap对象都存活较多;
检查代码后发现并未有显示的使用该两类类型,怀疑时是String字符串过多导致的;
2.其次使用JDK自带的分析工具:jmap -dump:format=b,file=文件名 [pid] 导出OOM时的dump日志;
导出时间非常慢,且占用线上系统的CPU,导致CPU达到100%
3.使用jstat -gc pid /jstat -gcutil pid 查看gc的状况
发现gc和fgc的都非常多,特别是fgc已经达到1000多次;
初步解决方案:(2月26日)
最后仍然是重启服务,-添加参数Xmx1024m -Xms:1024m
然后添加JVM参数(使用jinfo -flag可以在生产环境上直接添加)
jinfo -flag +HeapDumpBeforeFullGC pid
jinfo -flag +HeapDumpAfterFullGC pid
jinfo -flag +HeapDumpOnOutOfMemoryError pid
jinfo -flag +HeapDumpPath=/home/xxx/xxx pid 添加dump日志的目录(需要提前建好)
jinfo -flag -XX:+PrintGCDetails pid 开启gc日志
jinfo -flag -XX:+PrintGCDateStamps -Xloggc:/xxx/xxx 设置gc日志的目录
修改完成后第二天根据fgc产生的dump日志,加载到jvisualVM里面之后发现也是[C占用内存较多
下午 2点左右,监控线上服务时发现Old老年代的内存占用为300M,总大小为700M,经过一次FGC之后占用70M,这就比较正常了;
重点来了:
在2月26日添加完成JVM参数后,第二天同样的接口,FGC之前终于拿到了dump文件,大小是1.4G,接下来就是分析dump文件了,这里我选择了两个工具:
MAT与Jvisualvm
在使用体验来说JDK自带的Jvisualvm真的很垃圾,文件打开都要半个小时,果断放弃,转而使用MAT
导入dump文件以后如图
这里主要是看Leak Suspects:其他的几个指标在此也说明一下:
1. Histogram可以列出内存中的对象,对象的个数以及大小。
2. Dominator Tree可以列出那个线程,以及线程下面的那些对象占用的空间。
3.Top consumers通过图形列出最大的object。
4.Leak Suspects通过MA自动分析泄漏的原因。
打开Leak Suspects后可以看到线程堆栈如图
再继续找,找到是否有我们的业务代码。找到如图
这里其实已经定位到具体的业务代码了,但是MAT的强大之处就是可以定位究竟是什么大对象,
如图,这里已经可以看到了6W多个HashMap被Object[]引用,这里是内存占用的主要原因
OK,接下来可以看业务代码了
**
业务代码如下,只展示关键代码,这个接口是报表数据导出的接口,查询mysql后使用HashMap去接收结果集,
Object[]用于是用于写入报表工具类的入参;
查看服务器日志后,发现这条SQL有6W多条数据,而且在一分钟之内有人操作导出了两次,导致数据封装到HashMap里面,发生FGC
三、最终解决方案
1.加大堆内存 原来由512扩大到1024M;
2.HashMap改为JavaBean对象去封装结果集,因为HashMap底层是数组,还有其他的引用成员变量,更加有频繁的扩容,
查资料后发现HashMap在数据量的情况下内存占用比Java对象要大;
3.导出接口添加限流注解,防止在短时间内多次请求;
以下是限流代码:使用Guava的限流组件实现,当然也可以基于Redis的实现,或者自己实现一套
4.由于EasyExcel内存占用少,可以将poi换成阿里的EasyExcel,实现多条数据分页导出;
代码如下图片
代码如下
/**
* @author: Gabriel
* @date: 2020/2/18 12:03
* @description 自定义接口限流注解
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimitAnno {
/** 每秒放入令牌桶中的token */
double limitNum() default 20;
}
/**
* @author: Gabriel
* @date: 2020/2/18 12:07
* @description
*/
@Slf4j
@Aspect
@Component
public class RateLimitAspect {
/**
* 用来存放不同接口的RateLimiter(key为接口名称,value为RateLimiter)
*/
private ConcurrentHashMap map = new ConcurrentHashMap<>();
private RateLimiter rateLimiter;
@Autowired
private static ObjectMapper objectMapper = new ObjectMapper();
@Autowired
private HttpServletResponse httpServletResponse;
@Pointcut("@annotation(com.gabriel.stage.annotation.RateLimitAnno)")
public void rateLimit() {
}
/**
* 环绕通知
*
* @param joinPoint
* @return
* @throws Exception
*/
@Around("rateLimit()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Object obj = null;
//获取拦截的方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Object target = joinPoint.getTarget();
//获取注解信息
Method method = target.getClass().getMethod(signature.getName(), signature.getParameterTypes());
RateLimitAnno annotation = method.getAnnotation(RateLimitAnno.class);
double limitNum = annotation.limitNum();
//获取方法名
String functionName = signature.getName();
//获取类名
String className = signature.getDeclaringTypeName();
signature.getDeclaringTypeName();
if (StringUtils.isNotBlank(className)) {
className = StringUtils.substringAfterLast(className, ".");
}
//拼接类名和方法名,保证key唯一
String joinName = StringUtils.join(functionName, className);
//获取rateLimiter
if (map.containsKey(joinName)) {
rateLimiter = map.get(joinName);
} else {
map.put(joinName, RateLimiter.create(limitNum));
rateLimiter = map.get(joinName);
}
if (rateLimiter.tryAcquire()) {
obj = joinPoint.proceed();
} else {
System.err.println("接口限流,请求降级。。。。。。。。。。。。。。。。。");
throw new BusinessException(ResultCode.SERVER_ERROR);
}
return obj;
}
作者:听风是雨
原文地址:
https://www.cnblogs.com/july-sunny/p/12370615.html
上面就是本次文章的全部内容,感谢你的阅读,希望对你有帮助,也欢迎你点赞留言和转发~
关注我,不迷路,及时获取有趣有料内容,See you next good day~
相关推荐
- IDEA界面太丑??尝试一下这几个插件
-
前言IntelliJIDEA主要用于支持Java、Scala、Groovy等语言的开发工具,同时具备支持目前主流的技术和框架,擅长于企业应用、移动应用和Web应用的开发。IntelliJi...
- 小巧 Vue 页面滚动进度条组件ScrollProgress
-
今天给大家分享一个轻量级Vue.js全屏滚动进度条组件VueScrollProgress。vue-scroll-progress一款基于vue.js构建的页面滚动进度条组件,...
- 基于vue实现可视化拖拽编辑器,页面生成工具,提升前端开发效率
-
项目介绍基于vue实现的可视化拖拽编辑器,实现页面生成工具,提升前端开发效率。可以基层到移动端项目作为自定义json直接生成UI页面。项目特点功能特点...
- 优秀 vue+heyui 后端管理系统HeyUI-Admin
-
今天再给小伙伴们推荐一款成熟的企业中后台管理系统HEYUI-Admin。heyui-admin基于vue.js和heyui组件库构建的后台管理系统。包含基础表单/表格功能,拓展组件(图表、富文本编辑...
- 响应式 Vue.js 前端组件化框架Xvue-UI
-
今天给小伙伴们推荐一款超不错的Vue轻量级组件框架XVueUI。xvue-ui基于vue2.x构建的响应式前端组件化框架。轻量级、易于上手,提供...
- 《基于SpringBoot+Vue的在线视频系统设计与实现》开题报告
-
【计算机毕业设计案例】基于SpringBoot+Vue的在线视频系统设计与实现_哔哩哔哩_bilibili...
- 超好用 Vue.js 图片裁切组件Vue-ImgCutter
-
今天给小伙伴们分享一个超棒的Vue图片任意裁剪插件VueImgCutter。vue-img-cutter基于vue2.x构建的轻量级剪切图片组件。支持移动图像、放大缩小图片、任意移动图片、固定比...
- Vue 3 进阶用法:异步组件(vue 异步组件原理)
-
一、代码分割一个大型前端应用,如果所有代码都放在单一文件,体积会特别大,下载时间长,白屏时间久,用户体验差。...
- 源码补丁神器—patch-package(源码助手怎么用)
-
作者:张浩一、背景vue项目中使用vue-pdf第三方插件预览pdf,书写业务代码完美运行,pdf文件内容正常预览无问题。后期需求有变,业务需求增加电子签章功能。这个时候pdf文件的内容可以显示出...
- 经验分享:Vue2 项目升级 Vue3 + Element Plus,借助Deepseek手动升级
-
Vue3出来好久了,我开发的项目还在使用Vue2框架,一般情况下不考虑升级,但是最近需要接入工作流程引擎之类的,看了下Vue2生态下操作空间不是很好,那索性尝试升级Vue3吧。一番操作下来,升级成功,...
- 34K Star!史上最全JavaScript资源库 awesome-javascript
-
34KStar!史上最全JavaScript资源宝库大揭秘引言在GitHub上,有一个备受瞩目的JavaScript资源仓库,以其全面的内容和精心的分类,成为了众多开发者的必备参考。这个拥有超过...
- 基于 Vue.js 磁片栅格布局组件VueGridLayout
-
#头条创作挑战赛#今天给大家分享一个超优秀的vue.js拖拽栅格布局插件VueGridLayout。...
- 6款高颜值 Vue3 PC端UI组件库(vue3开发组件库)
-
马上到国庆了,还没学习或者想学习vue3的小伙伴们有安排上没?这次推荐几个比较流行的VUE3UI组件库,合理利用,又或者学习借鉴都是不错的选择。1、element-pluselement-plus...
- 高性能 vue.js+ztree 树形组件Vue-GiantTree
-
今天给大家分享一款超棒的Vue海量数据渲染树形组件VueGiantTree。vue-giant-tree基于ztree封装的Vue树形组件。轻松实现大数据高性能渲染,适合海量数据渲染场景。zTr...
- 【推荐】2024年推荐的6款开源免费 Vue 后台管理系统模板,建议收藏
-
前言在现今的软件开发领域,...
- 一周热门
- 最近发表
-
- IDEA界面太丑??尝试一下这几个插件
- 小巧 Vue 页面滚动进度条组件ScrollProgress
- 基于vue实现可视化拖拽编辑器,页面生成工具,提升前端开发效率
- 优秀 vue+heyui 后端管理系统HeyUI-Admin
- 响应式 Vue.js 前端组件化框架Xvue-UI
- 《基于SpringBoot+Vue的在线视频系统设计与实现》开题报告
- 超好用 Vue.js 图片裁切组件Vue-ImgCutter
- Vue 3 进阶用法:异步组件(vue 异步组件原理)
- 源码补丁神器—patch-package(源码助手怎么用)
- 经验分享:Vue2 项目升级 Vue3 + Element Plus,借助Deepseek手动升级
- 标签列表
-
- mydisktest_v298 (34)
- document.appendchild (35)
- 头像打包下载 (61)
- acmecadconverter_8.52绿色版 (39)
- word文档批量处理大师破解版 (36)
- server2016安装密钥 (33)
- mysql 昨天的日期 (37)
- parsevideo (33)
- 个人网站源码 (37)
- centos7.4下载 (33)
- mysql 查询今天的数据 (34)
- intouch2014r2sp1永久授权 (36)
- 先锋影音源资2019 (35)
- jdk1.8.0_191下载 (33)
- axure9注册码 (33)
- pts/1 (33)
- spire.pdf 破解版 (35)
- shiro jwt (35)
- sklearn中文手册pdf (35)
- itextsharp使用手册 (33)
- 凯立德2012夏季版懒人包 (34)
- 冒险岛代码查询器 (34)
- 128*128png图片 (34)
- jdk1.8.0_131下载 (34)
- dos 删除目录下所有子目录及文件 (36)