hprof 文件分析

2021-08-24,订单中心的一个项目出现了 OOM 异常,使用 MemoryAnalyzer 打开 dump 出来的 hprof 文件,可以看到 91.27% 的内存被一个超大对象javassist.ClassPool占用了。

那么,ClassPool是一个什么样的对象呢?我们知道,javassist 可以用来动态生成类,而生成的类就是放在这个ClassPool里面,具体以javassist.CtClass的形式存在。

所以,初步分析是 OOM 的原因是 javassist 生成的CtClass对象过多,即 javassist 生成了太多的类

为了验证我的猜想,我需要看看CtClass对象的内存情况,点击 Actions -> Histogram,如图。果然,这 2.3 G 的内存就是CtClass对象占用的。

接下来,我需要知道这些CtClass对象都是哪些类,点击 List objects -> with outgoing references。这时可以看到,项目里生成了大量的Orika_ProductionOrderUpdateCmd_ProductionOrderE_Mapper*

看着这些类的命名规则,是不是很熟悉呢?它们都是 orika 映射 bean 时动态生成的类。所以,大量的CtClass对象是由 orika 产生。orika 的原理我之前讲过(cglib、orika、spring等bean copy工具性能测试和原理分析),这里就不再赘述。

但是,orika 生成的映射类是可以复用的,为什么还会有这么多重复的映射类呢?

项目代码分析

在项目中找到唯一一处将ProductionOrderE映射成ProductionOrderUpdateCmd的地方。

在项目中,其他地方都是使用方法 1,唯独这里使用了方法 2,所以,有理由怀疑是不是方法 2 有 bug 呢?

  1. public class BeanUtils {
  2. // 方法1
  3. public static <S, D> D copy(S source, Class<D> destinationClass) {
  4. // ······
  5. }
  6. // 方法2
  7. public static <S, D> D copy(S source, Class<D> destinationClass, String excludeFields) {
  8. // ······
  9. }
  10. }

于是,我写了个简单的 demo,如下。我的假设是,使用方法 2 不会复用映射类,每 copy 一次就生成一个映射类,最终导致映射类过多。至于生成了几个映射类,我们可以通过输出映射类文件的方式来判断,使用启动参数-Dma.glasnost.orika.GeneratedSourceCode.writeSourceFiles=true -Dma.glasnost.orika.writeSourceFilesToPath=D:/tmp/orika可以输出映射类文件。

  1. public static void main(String[] args) {
  2. ProductionOrderE productionOrder = new ProductionOrderE();
  3. // 使用方法2
  4. ProductionOrderUpdateCmd copy = BeanUtils.copy(productionOrder, ProductionOrderUpdateCmd.class,
  5. "belongShop,belongOrg,userOperate,orgExtendInfo");
  6. ProductionOrderUpdateCmd copy2 = BeanUtils.copy(productionOrder, ProductionOrderUpdateCmd.class,
  7. "belongShop,belongOrg,userOperate,orgExtendInfo");
  8. // 使用方法1
  9. // ProductionOrderUpdateCmd copy3 = BeanUtils.copy(productionOrder, ProductionOrderUpdateCmd.class);
  10. // ProductionOrderUpdateCmd copy4 = BeanUtils.copy(productionOrder, ProductionOrderUpdateCmd.class);
  11. // zzs001
  12. }

运行方法,我们会发现,使用方法 1 时,只生成了一个映射类,而使用方法 2 时,生成了两个映射类。

以下是方法 2 的底层封装,这里使用ClassMapBuilder重新配置了ProductionOrderUpdateCmdProductionOrderE的映射关系,导致上一次 copy 时生成的CtNewClass对象不再复用。

所以,在使用 orika 时,A->B 的映射关系只能定义一次,不能反复定义

  1. private MapperFactory mapperFactory;
  2. public <S, D> D copy(S source, Type<S> from, Type<D> to, String excludeFields) {
  3. List<String> list = new ArrayList<>();
  4. if(excludeFields != null) {
  5. list = Arrays.asList(excludeFields.split(","));
  6. }
  7. ClassMapBuilder cb = this.mapperFactory.classMap(from, to);
  8. for(String s : list) {
  9. cb.exclude(s.trim());
  10. }
  11. cb.byDefault().register();
  12. return this.mapperFactory.getMapperFacade().map(source, from, to);
  13. // zzs001
  14. }

解决方案

经过上面的分析,解决方案就呼之欲出了,我们只需要在初始化时一次定义好ProductionOrderUpdateCmdProductionOrderE的映射关系就行了,如下。当然,方法 2 不能再用了。

  1. public class BeanUtils {
  2. static {
  3. ClassMapBuilder cb = BeanToolkit.instance().getMapperFactory().classMap(
  4. TypeFactory.valueOf(ProductionOrderE.class),
  5. TypeFactory.valueOf(ProductionOrderUpdateCmd.class)
  6. );
  7. cb.exclude("belongShop");
  8. cb.exclude("belongOrg");
  9. cb.exclude("userOperate");
  10. cb.exclude("orgExtendInfo");
  11. cb.byDefault().register();
  12. // zzs001
  13. }
  14. }

结语

经过以上分析,我们找到了 OOM 的原因,并较好地解决了问题。其实,我们应该更早的监控到异常,像上面说的这种会出现非堆内存过高的情况。

最后,感谢阅读,欢迎私信交流。

本文为原创文章,转载请附上原文出处链接:https://www.cnblogs.com/ZhangZiSheng001/p/15184914.html

记一次Orika使用不当导致的内存溢出的更多相关文章

  1. 避免因为Arcgis Server服务设置不当导致Oracle Process溢出的方法

    我之前写过一篇文章<arcsoc进程无限增长导致oracle processes溢出>(见链接:https://www.cnblogs.com/6yuhang/p/9379086.html ...

  2. JAVAFX之tableview界面实时刷新导致的内存溢出(自己挖的坑,爬着也要出来啊0.0)

    这几天遇到了一个问题,不幸开发的一个cs架构的工具,客户端开启后,内存一直在缓慢增长最终导致进程卡死,花了4天时间,终于爬出来了... 客户端通过timer定时器每30秒查询一次数据库以及一些业务逻辑 ...

  3. while死循环导致的内存溢出

    场景:新开发的功能内测,新调用了其它模块的接口,一如既往的点鼠标,计费,但是许久都没有响应页面遮罩一直锁着,最后抛出了以下异常 咋一看这个异常信息,不就是锁表了吗?把锁表进程Kill掉,再来一遍,结果 ...

  4. 由于使用JDBC ResultSet的滚动功能而导致的内存溢出

    前天一去公司,老大说,服务器全挂了! 最后排查了半天,结论是内存溢出! 在WAS的DUMP日志中,看得我头晕眼花,终于找到了罪魁祸首,原来是有同事写代码的时候使用了可滚动的结果集导致内存溢出. 什么是 ...

  5. Java之JVM调优案例分析与实战(2) - 集群间同步导致的内存溢出

    环境:一个基于B/S的MIS系统,硬件为两台2个CPU.8GB内存的HP小型机,服务器是WebLogic 9.2,每台机器启动了3个WebLogic实例,构成一个6个节点的亲合式集群. 说明:由于是亲 ...

  6. vue 中的router 配置问题 导致的内存溢出~~~

    最近的项目用到 vue, 各种踩坑中. 其中一个就是router映射表写的稍有不慎,就会出现内存溢出的问题, 而且也不会具体告诉你哪里出错,所以很是头疼~~~ 出错多了,发现了一些router的一些规 ...

  7. Ubuntu 16.04中XMind 8导致Java内存溢出的问题解决(硬盘卡死,桌面卡死)

    XMind使用的是Java进行开发,如果出现内存溢出的问题,那么一定是桌面快捷方式的问题,解决方法是直接修改快捷方式里面的内容,修改如下: [Desktop Entry] Encoding=UTF-8 ...

  8. Gson序列化问题导致的内存溢出,tip:Background sticky concurrent mark sweep GC freed

    问题原因,如果在json model里面放了非可序列化的对象就会导致这中问题,可序列化的就是那些基础数据类型和集合类型,如果在里面放个Android的Activity或者adapter这类类型字段,变 ...

  9. ext在web工程目录导致myeclipse内存溢出问题

    分类: Extjs2013-01-24 00:01 2068人阅读 评论(2) 收藏 举报 当在eclipse中的web工程中增加了extjs4,出现An internal error occurre ...

随机推荐

  1. linux学习之路第三天(vim和vi使用)

    vi和vim编辑器 vi和vim的三种常见模式 1.正常模式 在正常模式下,我们可以使用快捷键 以vim打开一个档案就直接进入一般模式了(这是默认的模式).在这个模式中,你可以使用 上下左右按键来移动 ...

  2. shell运维习题训练

    注:初学shell,以下为本人自己写的答案,如果有更好的,请指教! 1. 求2个数之和: 2. 计算1-100的和 3. 将一目录下所有的文件的扩展名改为bak 4.编译并执行当前目录下的所有.c文件 ...

  3. 使用 Cron4j 表达式 在 Solon 里开发定时任务

    cron4j 是一个轻量级的Java任务调度工具.cron4j-solon-plugin 是 solon 对 cron4j 的适配插件 添加 maven 引用 <dependency> & ...

  4. Java | 字符串缓冲区(StringBuilder)

    为什么要出现字符缓冲区 我们都知道,String类是不可变的,但是有的时候,我们要用到字符串的拼接,如果拼接的数量小的时候,还可以,但是如果拼接的数据量太大的话,内存的占用就太大了,所以这个时候再用S ...

  5. 如何处理RabbitMQ 消息堆积和消息丢失问题

    消息堆积 解决方案: 增加消费者或后台相关组件的吞吐能力 增加消费的多线程处理 根据不同的业务实现不同的丢弃任务,选择不同的策略淘汰任务 默认情况下,RabbitMQ消费者为单线程串行消费,设置并行消 ...

  6. Java程序设计当堂测试 9.20

    /*Java当堂测试 ATM机模拟系统由于学习的知识有限,不能完成所有课上项目,文件的应用没有完成,汇款转账功能也没有写,一些要求该退出的地方也没有写,基本功能还算完善*/ 1 package acc ...

  7. 算法基础~链表~排序链表的合并(k条)

    算法基础~链表~排序链表的合并(k条) 1,题意:已知k个已排序链表头结点指针,将这k个链表合并,合并后仍然为有序的,返回合并后的头结点. 2,方法之间时间复杂度的比较: 方法1(借助工具vector ...

  8. 你有没有乱用“leader”,担当是个好东西

    PS:此文为个人认知,不足处请多多批评. 近期在一线leader(经理)身上发现了几个case,然后又回想起前几年自己做的一些傻事,可能都属于明面上leader不会说什么,但私下会有情绪的类型: Ca ...

  9. 就想搞明白,component-scan 是怎么把Bean都注册到Spring容器的!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 忒复杂,没等搞明白大促都过去了! 你经历过618和双11吗?你加入过大促时候那么多复 ...

  10. tomcat日志详解

    1 tomcat 日志详解 1.1 tomcat 日志配置文件 tomcat 对应日志的配置文件:tomcat目录下的/conf/logging.properties. tomcat 的日志等级有:日 ...