JVM Metaspace内存溢出问题

news2024/9/22 5:25:47

更多内容,前往 IT-BLOG

一、现象

x项目线上环境因为jvmOOM的异常而报警,导致整个服务不可用并被拉出集群,现象如下:

当时的解决方案是增加metaspace的容量: -XX:MaxMetaspaceSize=512m, 从原来默认的256m改为512m, 虽然没有再出现oom,但这个只是临时解决方案,通过hickwall观察metaspace的使用情况还是在上升,后面随着业务访问量越来越大还是有可能达到阈值。

二、分析

Metaspace元空间主要是存储类的元数据信息, 我们的应用里加载的各种类描述信息,比如类名,属性,方法,访问限制等,按照一定的结构存储在Metaspace里,Metaspace空间增长是由于反射类加载,动态代理生成的类加载等导致的,也就是说Metaspace的大小和加载类的数据有关系, 加载的类越多,metaspace占用的内存也就越大。

根据当时的业务场景了解到是因为有个“用户服务”访问“订单详情”接口的访问量突然上升,以及查看clogeroor日志发现大部分都是"订单详情"接口先报出的这个问题:java.lang.OutOfMemoryError: Metaspace。我在测试环境的jvm里增加-XX:+TraceClassLoading -XX:+TraceClassUnloading记录下类的加载和卸载情况,然后通过jmeter多个线程调用"订单详情"接口模拟metaspace溢出的现象。

发现在catalina.out文件里输出的除了业务上用到的类外还有大量的反射类,如下

[Unloading class sun.reflect.GeneratedSerializetiononConstructorAccessor12 0x0000000100289809]
[Unloading class sun.reflect.GeneratedSerializetiononConstructorAccessor11 0x0000000100289603]
[Unloading class sun.reflect.GeneratedSerializetiononConstructorAccessor10 0x0000000100289556]
[Unloading class sun.reflect.GeneratedSerializetiononConstructorAccessor9 0x0000000100289543]
[Unloading class sun.reflect.GeneratedSerializetiononConstructorAccessor8 0x0000000100289526]
[Unloading class sun.reflect.GeneratedSerializetiononConstructorAccessor7 0x0000000100289453]
[Unloading class sun.reflect.GeneratedSerializetiononConstructorAccessor6 0x0000000100289451]
[Unloading class sun.reflect.GeneratedSerializetiononConstructorAccessor5 0x0000000100289333]
[Unloading class sun.reflect.GeneratedConstructorAccessor8 0x00000001001e3c28]
[Unloading class sun.reflect.GeneratedConstructorAccessor7 0x00000001001e3823]
[Unloading class sun.reflect.GeneratedConstructorAccessor6 0x00000001001e3422]
[Unloading class sun.reflect.GeneratedSerializetiononConstructorAccessor4 0x0000000100188e31]
[Unloading class sun.reflect.GeneratedSerializetiononConstructorAccessor3 0x0000000100188028]
[Unloading class sun.reflect.GeneratedSerializetiononConstructorAccessor2 0x0000000100187322]
[Unloading class java.lang.invoke.LambdaForm$BMH/1682092198 0x000000010010D428]
[Unloading class java.lang.invoke.LambdaForm$BMH/1682092658 0x000000010010D021]
[Unloading class java.lang.invoke.LambdaForm$BMH/1682096534 0x000000010010Ba28]
[Unloading class java.lang.invoke.LambdaForm$BMH/1682097434 0x000000010010D423]
[Unloading class java.lang.invoke.LambdaForm$BMH/1682096438 0x0000000100102424]
[Unloading class sun.reflect.GeneratedSerializetiononConstructorAccessor1 0x0000000100187c32]
[Loading class sun.reflect.GeneratedSerializetiononConstructorAccessor12 from __JVM_DefineClass__]
[Loading class sun.reflect.GeneratedSerializetiononConstructorAccessor11 from __JVM_DefineClass__]
[Loading class sun.reflect.GeneratedSerializetiononConstructorAccessor10 from __JVM_DefineClass__]
[Loading class sun.reflect.GeneratedSerializetiononConstructorAccessor9 from __JVM_DefineClass__]
[Loading class sun.reflect.GeneratedSerializetiononConstructorAccessor8 from __JVM_DefineClass__]
[Loading class sun.reflect.GeneratedSerializetiononConstructorAccessor7 from __JVM_DefineClass__]
[Loading class sun.reflect.GeneratedConstructorAccessor8 from __JVM_DefineClass__]
[Loading class sun.reflect.GeneratedSerializetiononConstructorAccessor6 from __JVM_DefineClass__]
[Loading class sun.reflect.GeneratedSerializetiononConstructorAccessor5 from __JVM_DefineClass__]
[Loading class sun.reflect.GeneratedSerializetiononConstructorAccessor4 from __JVM_DefineClass__]
[Loading class sun.reflect.GeneratedSerializetiononConstructorAccessor3 from __JVM_DefineClass__]
[Loading class sun.reflect.GeneratedSerializetiononConstructorAccessor2 from __JVM_DefineClass__]
[Loading class sun.reflect.GeneratedSerializetiononConstructorAccessor1 from __JVM_DefineClass__]
[Loading class sun.reflect.GeneratedConstructorAccessor7 from __JVM_DefineClass__]
[Loading class sun.reflect.GeneratedConstructorAccessor6 from __JVM_DefineClass__]
[Loading class sun.reflect.GeneratedConstructorAccessor5 from __JVM_DefineClass__]

这些反射类被频繁的加载和卸载是不正常的, 通过Arthas阿尔萨斯工具观察发现每次调用接口都是通过反射的方式实现的

目前我们的项目都是基于soa框架对外提供访问的, 从上图sun.reflect的调用者com.ctriposs.baiji.rpc这些命名也能看出来,

通过上图可以看出在调用底层接口时都是通过反射的方式获取类的实例, 查看com.ctriposs.baiji.rpc.client.ServiceClientBasegetInstance方法代码实现可以确认。

同样对底层接口返回的json数据反序列化时也会用到反射

public <T> T deserialize(Class<T> clazz, InputStream inputStream) throws IOException {
    Class<? extends SpecificRecord> ct = (Class<? extends SpecificRecord>) clazz;
    return (T) _serializer.deserialize(ct, inputStream);
}

继续跟代码可以看到这些反射的实现都会用到java.lang.Class里的ReflectionData对象

ReflectionData是个内部静态类被缓存起来,里面的属性就是我们做反射操作时需要用的属性Field,方法Method和构造函数等.

但是有个问题reflectionData是被SoftReference软引用修饰的,如下图:

如果是软引用的话在内存空间不足时就可能会被回收掉,如果回收掉,那下次再使用的话只能重新通过反射获取,而SoftReference是否被回收又跟SoftRefLRUPolicyMSPerMB参数的值有关系,查看我们线上JVM的配置发现-XX:SoftRefLRUPolicyMSPerMB这个参数设置的是0

SoftRefLRUPolicyMSPerMB这个参数大概意思是每1M空闲空间可保持的SoftReference对象的生存时长(单位是ms毫秒),LRU应该是Least Recently Used的缩写,最近最少使用的,这个值默认是1000ms, 如果被设置为0,就会导致软引用对象马上被回收掉,进而会导致重新频繁的生成新的类,而无法达到复用的效果,第三张图里大量的sun.reflect.GeneratedSerializationConstructorAccessor, GeneratedMethodAccessor就是这样产生的。我把这个参数改为-XX:SoftRefLRUPolicyMSPerMB=1000 (1秒), 发布到生产环境验证了下, 大概是6月10号14点发布,发布后就降下来了,到今天为止基本上比较稳定

下面是单台机器commited的曲线变化

这个是10.28.104.85metaspace变化曲线,调整后基本上没有再出现波动

下面这个是项目调整后的情况,这个是昨天11号16点发布到线上的

三、总结

【1】目前主要是通过修改JVM-XX:SoftRefLRUPolicyMSPerMB值来解决metaspace上升问题, 后续会持续观察变化,适当调整参数, 调整的规则可以参考下这篇文章: 【参考答案】:链接 。
【2】我们的应用需要大量RPC交互, 使用SOA,CDubbo都会遇到类似的问题,通过上面的源码分析可以看出这个是无法避免的(除非是换一种序列化协议,比如hessian,不走方法反射的方式来赋值)。包括本身使用的Spring框架很多地方也是通过反射实现的比如AOP, 还有我们埋点经常使用的JsonUtils工具, 通过dump文件也能看出来存在大量的属性拷贝和反射操作。

所以我们在平时的业务代码开发中如果遇到两个对象赋值的操作尽量少用反射的方式实现, 比如下面的代码里使用了

这里做的对象拷贝操作使用的是apache common-beanutils.jar中的BeanUtils, 这个类底层采用javabeans+反射实现,性能比较差,内存开销比较大,当系统高并发的情况容易导致Metaspace空间增长过快.这个我会维护到java开发规范里, 不建议这样使用.如果字段少的话直接赋值算了, 多的话可以使用CglibBeanCopier类,BeanCopier类底层是采用asm字节码操作方式来进行对象拷贝操作,性能和内存开销都比较小。

还有就是我们的DTO类里也有很多注解,这些注解可能是拷贝接口契约时遗留的, 类似下面:

这些@JsonProperty("....."), @XmlElement(".....") 注解业务逻辑里并不会用到, 但是如果用到了序列化或反序列化就会被反射使用,如下

所以建议大家都检查一下自己项目里的DTO类, 把没用的注解都删掉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/60055.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MyBatis ---- 动态SQL

MyBatis ---- 动态SQL1. if2. where3. trim4. choose、when、otherwise5. foreach6. SQL片段MyBatis 框架的动态 SQL 技术是一种根据特定条件动态拼接 SQL 语句的功能&#xff0c;它存在的意义是为了解决拼接 SQL 语句字符串时的痛点问题。 1. if /*** 根据条件查询员工信息if…

eBPF书籍和教程良心推荐

中文 BPF 性能工具&#xff08;书籍&#xff09;&#xff0c;作者 Brendan Gregg。本书的GitHub 回购。系统性能&#xff1a;企业与云&#xff0c;第 2 版 (2020)&#xff0c;作者&#xff1a;Brendan GreggJed Salazar 和 Natalia Reka Ivanko 的 eBPF 安全可观察性什么是 eB…

Metabase学习教程:系统管理-5

仪表板优化 如何使您的仪表板加载更快。 说到仪表板性能方面&#xff0c;基本上有四种方法可以让仪表板更快地加载&#xff1a; 要求更少的数据.缓存问题答案.组织数据以预测常见问题.提出有效的问题。图1。包含三个筛选器小部件的示例仪表板&#xff0c;它们使用Metabase附…

友宝在线在港交所上市申请“失效”:连续两年亏损,王滨为大股东

近日&#xff0c;贝多财经从港交所披露易了解到&#xff0c;Beijing UBOX Online Technology Corp.&#xff08;北京友宝在线科技股份有限公司&#xff0c;下称“友宝”或“友宝在线”&#xff09;的上市申请材料已经失效&#xff0c;目前已经无法查看。 其中&#xff0c;招股书…

期末前端web大作业:餐饮美食网站设计与实现——餐厅响应式网站制作html+css+javascript+jquery+bootstarp

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

Android开发的UI设计——Material Design

前言 Material Design 是用于指导用户在各种平台和设备上进行视觉、动作和互动设计的全面指南。如需在您的 Android 应用中使用 Material Design&#xff0c;请遵循 Material Design 规范中定义的准则&#xff0c;并使用 Material Design 支持库中提供的新组件和样式。 正篇 …

【软件安装】Ubuntu18.04及20.04中安装omnet++

注意&#xff1a;安装omnet首先看官方安装指导&#xff0c;不要直接百度。 omnet6.0.1官方安装指导omnet6.0只能在Ubuntu20.04及之后的版本使用&#xff0c;因为glibc版本不适配。 Ubuntu18.04安装omnet5.6.2 安装必要支持 更新apt-get $ sudo apt-get update安装依赖软件 $ s…

2022年四川省职业院校技能大赛网络搭建与应用赛项

2022年四川省职业院校技能大赛 网络搭建与应用赛项 &#xff08;一&#xff09; 技能要求 &#xff08;总分1000分&#xff09; 网络搭建与应用赛项执委会及专家组 2022年06月 竞赛说明 一、竞赛内容分布 “网络搭建与应用”竞赛共分三个部分&#xff0c;其中&#xff1a; 第一…

3个常用的损失函数

1. L2 loss &#xff08;均方损失&#xff09; 除以2就是可以在求导时2和1/2可以相乘抵消。 蓝色的曲线表示&#xff1a;y0时&#xff0c;变化预测值y’的函数。 绿色曲线表示&#xff1a;似然函数。e^-l。 是一个高斯分布。 橙色的线&#xff1a;表示损失函数的梯度 可以看到…

记录Windows下mysql更改my.ini文件中datadir路径后启动不起来的问题

1.mysql默认安装到了C盘&#xff0c;想将数据库存储路径改到别的盘下 将Data文件夹和日志复制到H盘 找到mysl服务&#xff0c;右键停止服务 更改my.ini文件中的路径 保存然后启动发现启动不起来 猜测原因1&#xff1a;文件夹没有权限 将文件夹权限给到所有的用户 右击 ”…

[附源码]Python计算机毕业设计Django青栞系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

Linux的进程创建

在Linux下面&#xff0c;对二进制程序有着严格的格式要求&#xff0c;这就是ELF&#xff0c;这个格式可以根据编译的结果不同&#xff0c;分为不同的格式。 ELF的三种类型 一&#xff1a;可重定位文件 在编译的时候&#xff0c;先做预处理工作&#xff0c;例如将头文件嵌入到…

VueX简单又详细的解读,看了就会用

一、VueX是什么 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 库。它采用集中式存储管理应用的所有组件的状态&#xff0c;并以相应的规则保证状态以一种可预测的方式发生变化。 二、为什么要用VueX “单向数据流”理念的简单示意&#xff1a; 当我们的应用遇到多个组…

Redis缓存

一.简介 缓存就是数据交换的缓冲区&#xff08;称作Cache [ kʃ ] &#xff09;&#xff0c;是存贮数据的临时地方&#xff0c;一般读写性能较高 二.添加Redis缓存 三.缓存更新策略 1.主动更新策略 Cache Aside Pattern(推荐) 需要调用者自己编码&#xff0c;但可控性高 Re…

SimSiam-Exploring Simple Siamese Pepresentation Learning

SimSiam Abstract 模型坍塌&#xff0c;在siamese中主要是输入数据经过卷积激活后收敛到同一个常数上&#xff0c;导致无论输入什么图像&#xff0c;输出结果都能相同。 而He提出的simple Siamese networks在没有采用之前的避免模型坍塌那些方法&#xff1a; 使用负样本lar…

K_A08_003 基于 STM32等单片机驱动L9110模块按键控制直流电机正反转加减速启停

目录 一、资源说明 二、基本参数 1、参数 2、引脚说明 三、驱动说明 L9110模块驱动时序 对应程序: PWM信号 四、部分代码说明 接线说明 1、STC89C52RCL9110模块 2、STM32F103C8T6L9110模块 五、基础知识学习与相关资料下载 六、视频效果展示与程序资料获取 七、项目主要…

【Android工具】群晖安卓客户端基础套件:Drive、video、Photos和DS video安卓TV客户端...

微信关注公众号 “DLGG创客DIY”设为“星标”&#xff0c;重磅干货&#xff0c;第一时间送达。最近终于把all in one搞起来了&#xff0c;all in one就是把一堆功能一堆软件装一台主机里。。all in one&#xff08;以后简称AIO&#xff09;相关内容回头慢慢聊。今天先聊聊群晖&…

从一个demo说elf文件

本文的demo是在linux环境下编译解析的&#xff0c;cpu是x86-64 首先我们先写一个功能简单的demo-SimpleSection.c。这个demo中有一个func1函数用来打印数据&#xff0c;一个已经初始化的全局变量global_init_var和未初始化的全局变量global_uninit_var&#xff0c;一个已初始化…

使用TS 封装 自定义hooks,实现不一样的 CRUD

文章目录使用TS 封装 自定义hooks&#xff0c;实现不一样的 CRUD自定义 hooks文件结构type.tsuseDelData.ts使用useFetchList.ts使用useInsert.ts使用部分的接口方法使用TS 封装 自定义hooks&#xff0c;实现不一样的 CRUD 这一篇主要是记录 查缺补漏&#xff0c;提升自己的 强…

三、内存管理 (一)存储器管理

目录 1.1程序运行的基本过程 1.1.1 编辑、编译、链接、装入 1.1.2链接的三种方式 1.1.3装入的三种方式 1.2内存管理基本概念 1.2.1内存保护 1.2.2内存空间扩充 1.2.3地址转换功能 1.2.4内存空间的分配与回收 1.2.4.1连续分配管理方式 1.2.4.1.1单一连续分配 1.2.4.1…