元空间OOM排查记录【详解】

一、 背景

项目开发数据源全部通过访问其他平台开放的RestFul接口获取,接口查询数据并不能像查询本地数据库一样自由写SQL般灵活,导致大量数据基于内存处理,随着需求增多,最终导致了项目组件出现了元空间OOM异常。

二、 现象

  1. 浏览器登录平台访问组件页面后无法加载出来,查看日志发现元空间内存溢出。在这里插入图片描述
  2. 查看组件原来JVM配置,发现分配的元空间内存与堆内存过小。在这里插入图片描述
  3. 增加组件内存配置后,组件页面可正常访问,但是内存仍然高居不下,占用峰值达到4G多,线程数210多条。

三、 排查

  1. jstat –gcutil 命令排查jvm各个分区的内存占用率,其中M与CCS两个数值最能凸显问题。在这里插入图片描述
    - M:元数据区域(Metaspace)的空间利用率为96.38%。

    - CCS:压缩类空间的空间利用率为89.98%。

    元空间主要用于存储Java类的元数据信息,如类定义、常量池、方法表等。元空间由JVM在启动时加载类时生成,并随着类的加载和卸载而动态调整。

    压缩类是指在JVM中经过压缩的类元数据,它们主要用于存储类的结构信息、常量池和方法表等。压缩类空间与元空间的关系在于:压缩类是元空间的一部分,它们存储了类的结构信息、常量池和方法表等。

    元空间利用率与压缩类空间利用率如此之高,说明在不断的访问页面时候会创建大量的类对象。

  2. jmap -histo:live | head –n 100命令反复查看堆内存直方图,发现组件相关的占用内存较大的类主要有两处:com.xxx.xxx.rbcd.xxx…xxx.AllAircraftHistoryInfo和springfox.xxx…xxx.PropertySpecification。

    经排查,AllAircraftHistoryInfo对象占用内存较大是因为页面每5秒调用一次该类相关业务接口,而该接口将大量数据置于内存处理导致。PropertySpecification类是Swagger框架中的一部分,用于描述API文档中的属性规范。它提供了属性的各种元数据,例如名称、数据类型、格式、描述等,定义在API的请求和响应模型中使用的属性,也就是在生产环境中也开启了Swagger功能。在这里插入图片描述
    在这里插入图片描述

  3. jmap -dump:format=b,file=dump.hprof 生成该组件进程的二进制堆转储文件,通过MAT工具分析线程对于内存的占用情况。经排查,该进程中存在大量的undertow的io线程和业务处理线程,可以看到每个线程的shallow heap与retained heap直接或间接的占用一定内存空间。在这里插入图片描述

    经查阅源代码发现undertow服务器默认的io线程数量等于服务器的cpu核数work-io数量等于io线程数乘以8。在这里插入图片描述

四、 解决

  1. 停止页面每5秒调用业务接口,改为访问页面调用1次。

  2. 禁用生产环境的swagger。

  3. 添加underto线程配置。

    在这里插入图片描述

  4. 查验堆内存直方图,已经看不到占用内存较大业务类与swagger类。jstack 查看undertow线程io数量等于8。在这里插入图片描述

五、 复现

  1. 经过上述排查与解决后,频繁点击页面各个功能,重新查看元空间内存占用情况,发现元空间利用率与压缩类空间的空间利用率仍然高居不下,也就是上述排查并没有定位到问题根源。

  2. 采用VisualVM工具分析dump文件,根据类类型分组并统计数量。在这里插入图片描述
    在这里插入图片描述
    可以看出,该组件在运行过程中创建了大量的反射类加载器(jdk.internal.reflect.DelegatingClassLoader)。而元空间其元数据的生命周期与其对应的类加载器相同,只要类的类加载器是存活的,在Metaspace中的类元数据也是存活的,不能被回收。

    排查代码后发现,程序中充斥着大量的BeanUtils.copyProperties()属性复制方法,这些方法都是采用反射技术实现的。

    建议开发者采用mapstruct工具代替BeanUtils.copyProperties()方法实现实例间属性赋值。

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倔强的初学者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值