小胖有技能|记录一次生产上的OOM解决过程( 二 )

文章图片
再继续找 , 找到是否有我们的业务代码 。 找到如图

文章图片
这里其实已经定位到具体的业务代码了 , 但是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/1812:03*@description自定义接口限流注解*/@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public@interfaceRateLimitAnno{/**每秒放入令牌桶中的token*/doublelimitNum()default20;}/***@author:Gabriel*@date:2020/2/1812:07*@description*/@Slf4j@Aspect@ComponentpublicclassRateLimitAspect{/***用来存放不同接口的RateLimiter(key为接口名称 , value为RateLimiter)*/privateConcurrentHashMap<String,RateLimiter>map=newConcurrentHashMap<>();privateRateLimiterrateLimiter;@AutowiredprivatestaticObjectMapperobjectMapper=newObjectMapper();@AutowiredprivateHttpServletResponsehttpServletResponse;@Pointcut(''@annotation(com.gabriel.stage.annotation.RateLimitAnno)'')publicvoidrateLimit(){}/***环绕通知**@paramjoinPoint*@return*@throwsException*/@Around(''rateLimit()'')publicObjectaround(ProceedingJoinPointjoinPoint)throwsThrowable{Objectobj=null;//获取拦截的方法签名MethodSignaturesignature=(MethodSignature)joinPoint.getSignature();Objecttarget=joinPoint.getTarget();//获取注解信息Methodmethod=target.getClass().getMethod(signature.getName(),signature.getParameterTypes());RateLimitAnnoannotation=method.getAnnotation(RateLimitAnno.class);doublelimitNum=annotation.limitNum();//获取方法名StringfunctionName=signature.getName();//获取类名StringclassName=signature.getDeclaringTypeName();signature.getDeclaringTypeName();if(StringUtils.isNotBlank(className)){className=StringUtils.substringAfterLast(className,''.'');}//拼接类名和方法名,保证key唯一StringjoinName=StringUtils.join(functionName,className);//获取rateLimiterif(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(''接口限流 , 请求降级 。。。。。。。。。。。。。。。。。 '');thrownewBusinessException(ResultCode.SERVER_ERROR);}returnobj;}作者:听风是雨
推荐阅读
- 黄皮子谈科技|高通都做不到,华为又抢在了高通的前面!新芯片再创全球记录
- 鳄鱼|26张照片记录了残酷的大自然,这就是生存与弱肉强食
- 99今晚打老虎|微信聊天记录删除了怎么恢复?这几个软件可以解决问题
- 智能家|华为恢复微信聊天记录怎么弄
- 产业气象站|燧原科技被寄予行业领军企业希望,18个月AI训练芯片一次性流片成功创记录
- 小胖有技能|要不,让图灵“亲自”给你讲讲?,还没搞懂人工智能吧
- 产业气象站|微信记得删除这记录,不然别人能获取账号信息,不管什么手机
- 驱动中国|储璨璨:透过镜头记录那个不完美却真实的世界
- 小胖聊科技|小米有品上新众筹爆款走步机,售价仅千元,出道即巅峰
- 手机圈小胖|关于华为Mate40的那些料
