JVM Metaspace内存溢出排查与总结
一. 现象
前段时间公司线上环境的一个Java应用因为OOM的异常报警,导致整个服务不可用被拉出集群,本地模拟重现的现象如下:
当时的解决方案是增加metaspace的容量:-XX:MaxMetaspaceSize=500m
,从原来默认的256m改为500m,虽然没有再出现oom,但这个只是临时解决方案,通过公司的监控系统观察metaspace的使用情况还是在上升,而且后面随着业务访问量越来越大还是有可能达到阈值。
二. 分析
Metaspace元空间主要是存储类的元数据信息,我们的应用里加载的各种类描述信息,比如类名、属性、方法、访问限制等,按照一定的结构存储在Metaspace里。
由此可知metaspace空间增长是由于反射类加载,动态代理生成的类加载等导致的,也就是说Metaspace的大小和加载类的数据有关系,加载的类越多metaspace占用的内存也就越大。
因为了解当时的业务场景是因为有个邮件服务访问订单详情接口的访问量突然上升,以及查看log的eroor日志发现大部分都是订单详情接口先报出的这个问题:java.lang.OutOfMemoryError: Metaspace
这里我在测试环境Java应用的jvm里增加-XX:+TraceClassLoading
-XX:+TraceClassUnloading
记录下类的加载和卸载情况,然后通过jmeter多个线程调用订单详情接口模拟metaspace溢出的现象,发现在catalina.out文件里输出的除了业务上用到的类外还有大量的反射类,如下:
这些反射类被频繁的加载和卸载是不正常的,通过Arthas诊断工具(Java在线诊断利器之Arthas)观察调用链发现每次调用接口都是通过反射的方式实现的。
目前我们的项目都是基于SOA框架对外提供访问的,从上图sun.reflect
的调用者也能看出来
通过上图可以看出在调用底层接口时都是通过反射的方式获取类的实例,查看框架底层代码实现可以确认
同样对底层接口返回的json数据反序列化时也会用到反射
继续跟代码可以看到这些反射的实现都会用到java.lang.Class
里的ReflectionData
对象
ReflectionData
是个内部静态类被缓存起来,里面的属性就是我们做反射操作时需要用的属性Field
,方法Method
和构造函数等。但是有个问题reflectionData
是被SoftReference软引用修饰的,如下图
如果是软引用的话在内存空间不足时就可能会被回收掉,如果回收掉那下次再使用的话只能重新通过反射获取。
而SoftReference是否被回收又跟SoftRefLRUPolicyMSPerMB
参数的值有关系,查看我们线上JVM的配置发现XX:SoftRefLRUPolicyMSPerMB
这个参数设置的是0
SoftRefLRUPolicyMSPerMB
这个参数大概意思是每1M空闲空间可保持的SoftReference对象的生存时长(单位是ms毫秒),LRU是Least Recently Used的缩写,最近最少使用的。
这个值jvm默认是1000ms,如果被设置为0,就会导致软引用对象马上被回收掉,进而会导致重新频繁的生成新的类,而无法达到复用的效果。
上图里大量的sun.reflect.GeneratedSerializationConstructorAccessor,GeneratedMethodAccessor
就是这样产生的。
我把这个参数改回默认值-XX:SoftRefLRUPolicyMSPerMB=1000
(1秒),发布到生产环境验证了下,发布后就降下来了,到今天为止基本上趋于稳定
调整后基本上没有再出现波动
三. 总结
- 目前主要是通过修改JVM的
-XX:SoftRefLRUPolicyMSPerMB
值来解决metaspace上升问题,后续会持续观察变化,适当调整参数。至于这个参数之前为什么会被设置成0, 还需要找ops确认下。 - 我们的应用需要大量RPC交互,属于I/O密集型业务,使用SOA,Dubbo都会遇到类似的问题,通过上面的源码分析可以看出这个是无法避免的(除非是换一种序列化协议,比如
hessian
,不走方法反射的方式来赋值)包括本身使用的Spring框架很多地方也是通过反射实现的比如AOP,还有我们埋点经常使用的JsonUtils
工具,通过dump文件也能看出来存在大量的属性拷贝和反射操作。
所以我们在平时的业务代码开发中如果遇到两个对象赋值的操作尽量少用反射的方式实现,比如下面的代码:
这里做的对象拷贝操作使用的是apache common-beanutils.jar中的BeanUtils
,这个类底层采用javabeans+反射实现,性能比较差,内存开销比较大,当系统高并发的情况容易导致Metaspace空间增长过快,不建议这样使用。
如果字段少的话直接赋值就行了,多的话可以使用Cglib的BeanCopier
类,BeanCopier
类底层是采用asm字节码操作方式来进行对象拷贝操作,性能损耗和内存开销都比较小。
或者使用MapStruct这种帮你生成set
、get
方法的工具,效果会更好。
文章来源:javakk.com/160.html

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
梯度下降算法在机器学习中的工作原理
梯度下降算法在机器学习中的工作原理 作者|NIKIL_REDDY 编译|VK 来源|Analytics Vidhya 介绍 梯度下降算法是工业中最常用的机器学习算法之一。但这让很多新人感到困惑。 如果你刚刚接触机器学习,梯度下降背后的数学并不容易。在本文中,我的目的是帮助你了解梯度下降背后的直觉。 我们将快速了解成本函数的作用,梯度下降的解释,如何选择学习参数。 什么是成本函数 它是一个函数,用于衡量模型对任何给定数据的性能。成本函数将预测值与期望值之间的误差量化,并以单个实数的形式表示出来。 在对初始参数进行假设后,我们计算了成本函数。以降低代价函数为目标,利用梯度下降算法对给定数据进行参数修正。下面是它的数学表示: _LI.jpg) 什么是梯度下降 假设你在玩一个游戏,玩家在山顶,他们被要求到达山的最低点。此外,他们还蒙着眼睛。那么,你认为怎样才能到达湖边? 在你继续读之前,花点时间考虑一下。 最好的办法是观察地面,找出地面下降的地方。从这个位置开始,向下降方向迈出一步,重复这个过程,直...
- 下一篇
Elasticsearch数据库 | Elasticsearch-7.5.0应用搭建实战
Elasticsearch 是一个可用于分布式以及符合RESTful 风格的搜索和数据分析引擎。—— Elastic Stack 官网 搭建Elasticsearch的“那些事儿" 有一天,在黄金梅丽号的甲板上,韦柏告诉萨博,需要在接下来的项目开发过程中,运用到Elasticsearch数据库,主要用于黄金梅丽号上的各种设备采集数据实时查询,或许后期还会运用于分布式日志系统的搭建运用等,让萨博先做一次技术预研。于是,在萨博查询大量的资料发现,关于Elasticsearch数据库的搭建,网上的资料几乎是千篇一律,或者多数都是没有一个完整的流程。甚至,还发现,对于Elasticsearch数据库实际应用方面,大多数海贼只局限于ELK等这样的情况,就像是大多数海贼提到Redis,就只觉得这个玩意儿,只能用于缓存层面,但是实际上人家的功能强大到超出了海贼的想象空间。甚至于,萨博在一个阿里巴巴的地方,找到了关于Elasticsearch数据库免费试用的资源,但是对于免费的午餐,也许那一天收费之后,那就只能用一首“浪浪”来祭奠这万恶的黄金之恶。于是在萨博精心研究和分析发现,除了传统部署Elasti...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS8编译安装MySQL8.0.19
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Mario游戏-低调大师作品
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS6,7,8上安装Nginx,支持https2.0的开启