jvm的metaSpace内存溢出问题排查

jvm的内存溢出问题排查

  • [1. 背景](#1. 背景)
  • [2. 根据不能访问的同学的信息找到 报错日志](#2. 根据不能访问的同学的信息找到 报错日志)
  • [3. 分析现象与报错日志](#3. 分析现象与报错日志)
  • [4. 从监控查看jvm运行情况](#4. 从监控查看jvm运行情况)
  • [5. 根据监控观察到得情况初步分析。](#5. 根据监控观察到得情况初步分析。)
  • [6. 查看GC日志,metaSpace概要使用情况。](#6. 查看GC日志,metaSpace概要使用情况。)
  • [7. 使用arthas查看类加载情况](#7. 使用arthas查看类加载情况)
  • [7. 使用arthas堆中所有的类情况](#7. 使用arthas堆中所有的类情况)
  • [8. 使用heap dump,和 MAT(memory analyser tool)间接推断反射类被哪些业务代码加载,是否有问题。](#8. 使用heap dump,和 MAT(memory analyser tool)间接推断反射类被哪些业务代码加载,是否有问题。)
    • [8. 1 打印heap dump并下载到本地](#8. 1 打印heap dump并下载到本地)
    • [8. 2 安装配置MAT,并适配jdk](#8. 2 安装配置MAT,并适配jdk)
    • [8. 3 逐个分析每个反射生成的类的被引用情况](#8. 3 逐个分析每个反射生成的类的被引用情况)
    • [8. 4 分析与结论](#8. 4 分析与结论)

1. 背景

随着项目的发展,应用开始报错。

  1. 有时响应很慢
  2. 无端的报错且同样的接口,有的同学,可以访问,有些同学不能访问

2. 根据不能访问的同学的信息找到 报错日志

目前主要有两种

报错a:为

java 复制代码
Caused by: java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError:Metaspace

报错b:为

java 复制代码
org.springframework.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
 at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345)

3. 分析现象与报错日志

首先从报错日志开始分析

一个很直白,内存溢出,没有metaSpace了

一个比较隐晦:spring框架生成class时,无法生成,可能得原因有:堆空间不足或者metaSpace空间不足,又或者其他原因。

日志指示着,需要查看,metaSpace或者heap空间,需要查看jvm得运行状况。

响应很慢:看过接口,有些机器是正常响应,有些机器则响应得很慢,这不是业务代码得问题,跟机器有关系了。

同样得接口有的同学可以访问有的同学不能访问:其实是两个同学访问得机器不同。而访问报错得日志就是上面主要就是上面得两种之一。

得出结论:需要查看jvm运行情况,尤其是 metaSpace和heap空间

4. 从监控查看jvm运行情况

打开监控,发现

metaSpace空间 占用超高,有些机器98%了,最低得也有70%,metaSpace空间使用有问题。

cpu 有时飙高达到100%,且会持续一段时间,cpu飙高有问题。

fullGC 频繁进行,大于健康频率,而且metaSpace占用越高,fullGC进行就会越频繁, fullGC 有问题。

heap空间,占用率不高,只有30%左右,每个机器都差不多,正常。

5. 根据监控观察到得情况初步分析。

metaSpace是存放类信息得地方, 每次meatSpace满得时候会触发 fullGC。频繁fullGC。 而fullGC可能会执行stop-the-world指令会影响业务响应。观察cpu飙高得时间和fullGC高度重合,这可以理解为 fullGC导致cpu飙高。归根结底因该是 metaSpace使用有问题导致。

6. 查看GC日志,metaSpace概要使用情况。

发现 metaSpace 几乎是单调递增,很难被卸载,且99%以上的空间被NoClass 类型数据所使用。

且metaSpace 占用率一直维持再高位。

需要查看 metaSpace都是哪些类,都有哪些类加载,都是什么代码加载的。

7. 使用arthas查看类加载情况

观察到sun.reflect.DelegatingClassLoader 的加载了大量的类,且类都没有被卸载过。

不像是其他的类加载器,加载了很多,但都卸载了。

powershell 复制代码
classloader

我可以看到类似如下的

name numberOfInstances lodaderCountTotal
sun.reflect.DelegatingClassLoader xxxxxa xxxxxa

7. 使用arthas堆中所有的类情况

可以看到很多类都是反射生成的

powershell 复制代码
 sc sun.reflect.Generated*

8. 使用heap dump,和 MAT(memory analyser tool)间接推断反射类被哪些业务代码加载,是否有问题。

8. 1 打印heap dump并下载到本地

powershell 复制代码
ps -aux | grep java

jmap -dump:format=b,file=heap.hprof <pid>

8. 2 安装配置MAT,并适配jdk

MAT(memory analyser tool) 从eclipse 官网搜索 mat 下载。一般要求jdk至少为17,本地调换升级jdk就可以

如果dump文件很大,需要增加mat的内存占用。

如果解析报错,设置mat的preference 出现异常为 仅报warning,不中断。

8. 3 逐个分析每个反射生成的类的被引用情况

打开 histograme, 过滤出反射类(sun.reflect.Generated*)

右击类名称,list objects -> with incoming references

逐层点开引用,能到看具体的反射类名/方法名称 以及被什么业务类所引用

观察到

主要有:

  1. mybatis 反射生成DO (大量)
  2. spring容器加载时反射创建Bean(少量)
  3. FastJSON序列化与反序列化(有一些)
  4. spring通过反射强制知道具体类型的情况(controller的方法接收参数/返回参数,BeanUtils.copyProperties)(大量)

8. 4 分析与结论

接口很多,且表越来越多,mybatis和spring反射创建的类型信息会缓存起来,不会丢失,导致metaSpace不够用了。

增大jvm 的 metaSpace空间即可。

powershell 复制代码
-XX:MetaspaceSize=1g -XX:MaxMetaspaceSize=1g
相关推荐
m0_748554816 小时前
golang如何实现用户订阅偏好管理_golang用户订阅偏好管理实现总结
jvm·数据库·python
lee_curry6 小时前
第四章 jvm中的垃圾回收器
java·jvm·垃圾收集器
阿正呀7 小时前
Redis怎样实现本地缓存的高效失效通知
jvm·数据库·python
2501_901200538 小时前
mysql如何设置InnoDB引擎参数_优化innodb_buffer_pool
jvm·数据库·python
金銀銅鐵8 小时前
[java] 编译之后的记录类(Record Classes)长什么样子(上)
java·jvm·后端
m0_495496419 小时前
mysql处理复杂SQL性能_InnoDB优化器与MyISAM差异
jvm·数据库·python
forEverPlume9 小时前
PHP怎么使用Eloquent Attribute Composition属性组合_Laravel通过组合构建复杂属性【方法】
jvm·数据库·python
2301_8092047010 小时前
mysql在docker容器中如何部署_利用docker-compose快速启动
jvm·数据库·python
2301_8159019714 小时前
SQL如何将多行记录聚合成逗号分隔字符串_GROUP_CONCAT技巧
jvm·数据库·python
zjy2777714 小时前
Layui tab选项卡如何动态根据ID值进行程序化切换
jvm·数据库·python