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
相关推荐
sinat_255487813 小时前
泛型:类·学习笔记
java·jvm·笔记·学习
闻哥3 小时前
Docker Swarm 负载均衡深度解析:VIP vs DNSRR 模式详解
java·运维·jvm·docker·容器·负载均衡
yangyanping2010813 小时前
Go语言学习之对象关系映射GORM
jvm·学习·golang
Barkamin20 小时前
多线程简单介绍
java·开发语言·jvm
「QT(C++)开发工程师」1 天前
C++17三大实用特性详解:内联变量、std::optional、std::variant
jvm·c++
她说..1 天前
Java Object类与String相关高频面试题
java·开发语言·jvm·spring boot·java-ee
sinat_255487811 天前
泛型·学习笔记
java·jvm·数据库·windows·python
「QT(C++)开发工程师」1 天前
C++17三大实用特性详解:折叠表达式、结构化绑定与constexpr if
jvm·c++
minji...1 天前
Linux 多线程(五)用C++语言以面向对象方式封装线程
linux·运维·服务器·网络·jvm·数据库