记一次Orika使用不当导致的内存溢出
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 呢?
public class BeanUtils {
// 方法1
public static <S, D> D copy(S source, Class<D> destinationClass) {
// ······
}
// 方法2
public static <S, D> D copy(S source, Class<D> destinationClass, String excludeFields) {
// ······
}
}
于是,我写了个简单的 demo,如下。我的假设是,使用方法 2 不会复用映射类,每 copy 一次就生成一个映射类,最终导致映射类过多。至于生成了几个映射类,我们可以通过输出映射类文件的方式来判断,使用启动参数-Dma.glasnost.orika.GeneratedSourceCode.writeSourceFiles=true -Dma.glasnost.orika.writeSourceFilesToPath=D:/tmp/orika可以输出映射类文件。
public static void main(String[] args) {
ProductionOrderE productionOrder = new ProductionOrderE();
// 使用方法2
ProductionOrderUpdateCmd copy = BeanUtils.copy(productionOrder, ProductionOrderUpdateCmd.class,
"belongShop,belongOrg,userOperate,orgExtendInfo");
ProductionOrderUpdateCmd copy2 = BeanUtils.copy(productionOrder, ProductionOrderUpdateCmd.class,
"belongShop,belongOrg,userOperate,orgExtendInfo");
// 使用方法1
// ProductionOrderUpdateCmd copy3 = BeanUtils.copy(productionOrder, ProductionOrderUpdateCmd.class);
// ProductionOrderUpdateCmd copy4 = BeanUtils.copy(productionOrder, ProductionOrderUpdateCmd.class);
// zzs001
}
运行方法,我们会发现,使用方法 1 时,只生成了一个映射类,而使用方法 2 时,生成了两个映射类。

以下是方法 2 的底层封装,这里使用ClassMapBuilder重新配置了ProductionOrderUpdateCmd和ProductionOrderE的映射关系,导致上一次 copy 时生成的CtNewClass对象不再复用。
所以,在使用 orika 时,A->B 的映射关系只能定义一次,不能反复定义。
private MapperFactory mapperFactory;
public <S, D> D copy(S source, Type<S> from, Type<D> to, String excludeFields) {
List<String> list = new ArrayList<>();
if(excludeFields != null) {
list = Arrays.asList(excludeFields.split(","));
}
ClassMapBuilder cb = this.mapperFactory.classMap(from, to);
for(String s : list) {
cb.exclude(s.trim());
}
cb.byDefault().register();
return this.mapperFactory.getMapperFacade().map(source, from, to);
// zzs001
}
解决方案
经过上面的分析,解决方案就呼之欲出了,我们只需要在初始化时一次定义好ProductionOrderUpdateCmd和ProductionOrderE的映射关系就行了,如下。当然,方法 2 不能再用了。
public class BeanUtils {
static {
ClassMapBuilder cb = BeanToolkit.instance().getMapperFactory().classMap(
TypeFactory.valueOf(ProductionOrderE.class),
TypeFactory.valueOf(ProductionOrderUpdateCmd.class)
);
cb.exclude("belongShop");
cb.exclude("belongOrg");
cb.exclude("userOperate");
cb.exclude("orgExtendInfo");
cb.byDefault().register();
// zzs001
}
}
结语
经过以上分析,我们找到了 OOM 的原因,并较好地解决了问题。其实,我们应该更早的监控到异常,像上面说的这种会出现非堆内存过高的情况。
最后,感谢阅读,欢迎私信交流。
本文为原创文章,转载请附上原文出处链接:https://www.cnblogs.com/ZhangZiSheng001/p/15184914.html
记一次Orika使用不当导致的内存溢出的更多相关文章
- 避免因为Arcgis Server服务设置不当导致Oracle Process溢出的方法
我之前写过一篇文章<arcsoc进程无限增长导致oracle processes溢出>(见链接:https://www.cnblogs.com/6yuhang/p/9379086.html ...
- JAVAFX之tableview界面实时刷新导致的内存溢出(自己挖的坑,爬着也要出来啊0.0)
这几天遇到了一个问题,不幸开发的一个cs架构的工具,客户端开启后,内存一直在缓慢增长最终导致进程卡死,花了4天时间,终于爬出来了... 客户端通过timer定时器每30秒查询一次数据库以及一些业务逻辑 ...
- while死循环导致的内存溢出
场景:新开发的功能内测,新调用了其它模块的接口,一如既往的点鼠标,计费,但是许久都没有响应页面遮罩一直锁着,最后抛出了以下异常 咋一看这个异常信息,不就是锁表了吗?把锁表进程Kill掉,再来一遍,结果 ...
- 由于使用JDBC ResultSet的滚动功能而导致的内存溢出
前天一去公司,老大说,服务器全挂了! 最后排查了半天,结论是内存溢出! 在WAS的DUMP日志中,看得我头晕眼花,终于找到了罪魁祸首,原来是有同事写代码的时候使用了可滚动的结果集导致内存溢出. 什么是 ...
- Java之JVM调优案例分析与实战(2) - 集群间同步导致的内存溢出
环境:一个基于B/S的MIS系统,硬件为两台2个CPU.8GB内存的HP小型机,服务器是WebLogic 9.2,每台机器启动了3个WebLogic实例,构成一个6个节点的亲合式集群. 说明:由于是亲 ...
- vue 中的router 配置问题 导致的内存溢出~~~
最近的项目用到 vue, 各种踩坑中. 其中一个就是router映射表写的稍有不慎,就会出现内存溢出的问题, 而且也不会具体告诉你哪里出错,所以很是头疼~~~ 出错多了,发现了一些router的一些规 ...
- Ubuntu 16.04中XMind 8导致Java内存溢出的问题解决(硬盘卡死,桌面卡死)
XMind使用的是Java进行开发,如果出现内存溢出的问题,那么一定是桌面快捷方式的问题,解决方法是直接修改快捷方式里面的内容,修改如下: [Desktop Entry] Encoding=UTF-8 ...
- Gson序列化问题导致的内存溢出,tip:Background sticky concurrent mark sweep GC freed
问题原因,如果在json model里面放了非可序列化的对象就会导致这中问题,可序列化的就是那些基础数据类型和集合类型,如果在里面放个Android的Activity或者adapter这类类型字段,变 ...
- ext在web工程目录导致myeclipse内存溢出问题
分类: Extjs2013-01-24 00:01 2068人阅读 评论(2) 收藏 举报 当在eclipse中的web工程中增加了extjs4,出现An internal error occurre ...
随机推荐
- Could not connect to 'xxx.xx.xx.xxx' (port 22): Connection failed.
刚刚使用xshell好好的,突然注销账号,准备重新连接突然连不上了. 这就很尴尬了,对我这种linux菜鸟只能去百度了,终于解决了,赶紧记录下这个坑 1.先登陆虚拟机,输入这段命令 查看ssh服务是否 ...
- 文末送书四本 | 这篇Java反射机制太经典!不看后悔!
先看再点赞,给自己一点思考的时间,如果对自己有帮助,微信搜索[程序职场]关注这个执着的职场程序员. 价值:Java技能,面试经验指导,简历优化,职场规划指导,技能提升方法,讲不完的职场故事,个人成长经 ...
- mysql Authentication plugin 'caching_sha2_password' is not supported问题处理
使用mysql8.0版本,登录失败,提示 Authentication plugin 'caching_sha2_password' is not supported. 原因是在MySQL 8.0以后 ...
- spring boot框架相关知识
1.spring:一个轻量级的控制反转和面向切面的容器,专业的开发Web项目的开源框架. spring mvc:是基于spring的mvc框架,属于一个企业WEB开发的MVC框架,涵盖面包括前端 ...
- 【spring源码系列】之【Bean的初始化】
只要不放弃,希望迟早都会到来! 1. Bean的初始化 如果把bean的生命周期看作一个婴儿诞生过程的,那么创建实例相当于婴儿从母体出来,一丝不挂光秃秃:属性赋值相当于给宝宝的头带帽子,上身穿衣服.下 ...
- 手把手0基础Centos下安装与部署paddleOcr 教程
!!!以下内容为作者原创,首发于个人博客园&掘金平台.未经原作者同意与许可,任何人.任何组织不得以任何形式转载.原创不易,如果对您的问题提供了些许帮助,希望得到您的点赞支持. 0.paddle ...
- 聊聊 Spring AOP 的不为常知的“秘事”
Spring AOP 在我们日常开发中扮演了一个非常重要的角色,对于如何使用 AOP 相信很多人已经不陌生,但其中有一些点却容易被我们忽视,本节我们结合一些"不为常知"的问题展开讨 ...
- linux系统安装+windows系统安装
linux 1.格式化U盘 打开管理员命令提示符 diskpart list disk select disk 2 clean create partition primary format fs=f ...
- 【贪心+排序】排队接水 luogu-1223
题目描述 有n个人在一个水龙头前排队接水,假如每个人接水的时间为Ti,请编程找出这n个人排队的一种顺序,使得n个人的平均等待时间最小. 分析 注意要开longlong AC代码 #include &l ...
- [考试总结]noip模拟7
为啥博客园 \(\LaTeX\) 老挂???! \(\huge{\text{菜}}\) 刚开始写 \(T1\) 的时候,在看到后缀前缀之后,直接想到 \(AC\) 自动机,在画了半个 \(trie\) ...