Metaspace 引起的 FullGC 问题排查过程及解决方案

点击蓝色“程序猿DD”关注我哟

加个“星标”,不忘签到哦


640?wx_fmt=jpeg

转载自公众号:字节观



关注我,回复口令获取可获取独家整理的学习资料:

001 :领取《Spring Boot基础教程》

002 :领取《Spring Cloud基础教程》


最近新上线的系统偶尔会报FullGC时间过长(>1s)的告警,查看GC日志,如下图所示:


640?wx_fmt=png


看到GC日志,我第一时间关注到的不是GC耗时,而是GC触发的原因:Metadata GC Threshold。也就是FullGC触发的原因是因为Metaspace大小达到了GC阈值。在监控系统里面看了一下Metaspace的大小变化趋势,如下图所示:


640?wx_fmt=png


按照以往的经验,Metaspace在系统稳定运行一段时间后占用空间应该比较稳定才对,但是从上图来看,Metaspace显然是呈现大幅波动。为什么呢? 


相关知识


我们知道Metaspace主要存储类的元数据,比如我们加载了一个类,那么这个类的信息就会按照一定的数据结构存储在Metaspace中。Metaspace的大小和加载类的数目有很大关系,加载的类越多,Metaspace占用内存也就越大。


Metaspace被分配于堆外空间,默认最大空间只受限于系统物理内存。跟它相关的比较重要的两个JVM参数:

 
 
-XX:MetaspaceSize -XX:MaxMetaspaceSize。

MaxMetaspaceSize大家从名字也能猜到是指Metaspace最大值,而MetaspaceSize可能就比较容易让人误解为是Metaspace的最小值,其实它是指Metaspace扩容时触发FullGC的初始化阈值,在GC后该值会被动态调整:如果本次GC释放了大量空间,那么就适当降低该值,如果释放的空间较小则适当提高该值,当然它的值不会大于MaxMetaspaceSize.


另外一个相关知识是:Metaspace中的类需要满足什么条件才能够被当成垃圾被卸载回收?条件还是比较严苛的,需同时满足如下三个条件的类才会被卸载:

1、该类所有的实例都已经被回收;

2、加载该类的ClassLoader已经被回收;

3、该类对应的java.lang.Class对象没有任何地方被引用。


排查过程


我们可以回过头再细看GC日志,可以看出Metaspace已使用内存在FullGC后明显变小(372620K -> 158348K),说明Metaspace经过FGC后卸载了很多类。从这点来看,我们有理由怀疑系统可能在频繁地生成大量”一次性“的类,导致Metaspace所占用空间不断增长,增长到GC阈值后触发FGC。


640?wx_fmt=png


那么这些被回收的类是什么呢?为了弄清楚这点,我增加了如下两个JVM启动参数来观察类的加载、卸载信息:

 
 
-XX:TraceClassLoading -XX:TraceClassUnloading

加了这两个参数后,系统跑了一段时间,从Tomcat的catalina.out日志中发现大量如下的日志:


640?wx_fmt=png

640?wx_fmt=png


到此基本可以确定Metaspace增长的元凶是这些类,那么这些类sun.reflect.GeneratedSerializationConstructorAccessorXXX是干嘛的呢?又是从哪里引进来的呢?我也是一脸懵逼~~ 


根据类名Google了一把,找到了@寒泉子写的《从一起GC血案谈到反射原理》,这篇文章对这些类的来源解释得很透彻。在这里我简单总结如下:


 
 
Method method = XXX.class.getDeclaredMethod(xx,xx);	
method.invoke(target,params);


这些类的来源是来自反射,类似上面所示的反射代码应该大家都写过或者看过,我们常用的大多数框架比如Spring、Dubbo等都大量使用反射。出于性能的考虑,JVM会在反射代码执行一定次数后,通过动态生成一些类来将”反射调用”变为“非反射调用”,以达到性能更好。而这些动态生成的类的实例是通过软引用SoftReference来引用的。


我们知道,一个对象只有软引用SoftReference,如果内存空间不足,就会回收这些对象的内存;如果内存空间足够,垃圾回收器不会回收它。只要垃圾回收器没有回收它,该对象就可以被使用。那么究竟在什么时候会被回收呢?


SoftReference中有一个全局变量clock代表最后一次GC的时间点,有一个属性timestamp,每次访问SoftReference时,会将timestamp其设置为clock值。


当GC发生时,以下几个因素影响SoftReference引用的对象是否被回收:


1、SoftReference对象实例多久未访问,通过clock - timestamp得出对象大概有多久未访问;

2、内存空闲空间的大小;

3、SoftRefLRUPolicyMSPerMB常量值;


是否保留SoftReference引用对象的判断参考表达式,true为不回收,false为回收:


 
 
clock - timestamp <= freespace * SoftRefLRUPolicyMSPerMB 

说明:

clock - timestamp:最后一次GC时间和SoftReference对象实例timestamp的属性的差。就是这个SoftReference引用对象大概有多久未访问过了。


freespace:JVMHeap中空闲空间大小,单位为MB。


SoftRefLRUPolicyMSPerMB:每1M空闲空间可保持的SoftReference对象生存的时长(单位ms)。这个参数就是一个常量,默认值1000,可以通过参数:-XX:SoftRefLRUPolicyMSPerMB进行设置。


查看了一下我们系统的JVM参数配置,发现我们把SoftRefLRUPolicyMSPerMB设置为0了,这样就导致软引用对象很快就被回收了。进而导致需要频繁重新生成这些动态类。


为了验证这个猜测,我把SoftRefLRUPolicyMSPerMB改成了6000进行观察,发现果然猜得没错。系统启动后不久Metaspace的使用空间基本保持不变了,运行几天后也没再出现因为Metaspace大小达到阈值而触发FGC。至此问题解决。


References


[1] 假笨说-从一起GC血案谈到反射原理: https://mp.weixin.qq.com/s/5H6UHcP6kvR2X5hTj_SBjA?

[2] Java的强引用,软引用,弱引用,虚引用及其使用场景: http://blogxin.cn/2017/09/16/java-reference/


推荐阅读


活动介绍自律到极致-人生才精致:第8期

活动奖励:《小灰的算法之旅》* 10

扫描下面二维码签到参与

640?wx_fmt=png

关注我,加个星标,不忘签到哦~


2019

与大家聊聊技术人的斜杠生活

640?wx_fmt=png

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当遇到Metaspace的OOM异常时,可以尝试以下排查和处理方法: 1. 增加Metaspace内存空间: - 通过调整JVM参数来增加Metaspace的内存空间,可以使用`-XX:MetaspaceSize`和`-XX:MaxMetaspaceSize`参数来设置初始大小和最大大小。 - 适当增加Metaspace的内存空间可以缓解OOM异常,但需要注意不要过度分配内存导致其他部分受影响。 2. 检查是否存在大量动态生成的类: - 使用CGLIB、反射等方式动态生成类会消耗Metaspace的内存。 - 检查代码中是否存在频繁动态生成类的逻辑,考虑优化或减少动态生成类的使用。 3. 检查字符串常量的使用: - Metaspace也存储字符串常量,过多的字符串常量会增加Metaspace的内存压力。 - 检查代码中是否存在大量重复的字符串常量,可以使用字符串常量池或intern()方法来避免重复创建字符串对象。 4. 分析堆栈信息和内存快照: - OOM异常发生时,记录相关堆栈信息和内存快照。 - 使用工具(如MAT、VisualVM等)分析堆栈信息和内存快照,找出可能存在的内存泄漏问题。 5. 优化代码和资源管理: - 检查代码中是否存在不必要的类加载、资源加载或资源未释放的情况。 - 优化代码逻辑,避免重复加载和使用不必要的资源。 6. 升级JDK版本: - Metaspace的实现在不同的JDK版本中可能会有差异,升级到较新的JDK版本可能会提供更好的Metaspace管理和性能。 7. 参考官方文档和社区资源: - 参考Java官方文档、Oracle官方文档以及相关社区资源,了解Metaspace的工作原理和最佳实践。 - 在问题解决过程中,可以参考其他开发者的经验分享和解决方案。 重要的是,处理Metaspace的OOM异常需要综合考虑代码、配置和环境等多个因素,根据具体情况采取相应的措施。如果问题持续存在或难以解决,可以考虑寻求专业的Java性能优化工具或咨询服务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值