前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Metaspace内存不足导致FGC问题排查

Metaspace内存不足导致FGC问题排查

作者头像
LiosWong
发布于 2020-09-01 09:39:43
发布于 2020-09-01 09:39:43
3.8K00
代码可运行
举报
文章被收录于专栏:后端沉思录后端沉思录
运行总次数:0
代码可运行

事件回顾

清楚的记得是2020/7/25 14:34分左右,周六的下午,我还在公司苦逼的加班中,突然钉钉告警群里出现大量应用OP的dubbo超时调用、空指针异常,异常中间还有Metaspace元空间不足等异常:

代码语言:javascript
代码运行次数:0
运行
复制
o.a.c.f.l.ListenerContainer 98 [ERROR] Listener \(org.apache.curator.framework.recipes.cache.PathChildrenCache$3@7edb7fd5) threw an exception

java.lang.OutOfMemoryError: Metaspace

错误类型:【oom】

告警内容:2020-07-25 15:05:05:113 d5f54db7c1ca49ab85b9f54cde234bd1 c.c.d.l.w.DriverTraceWriterUtil 39 [ERROR] driver trace writer to file fail,ex:[{}]

java.lang.RuntimeException: by java.lang.ClassFormatError: Metaspace

at com.caocao.dc

再紧接着,发现我们应用OP的服务器大量FullGC,先一台发生,很快第二台开始FGC,第10台...

代码语言:javascript
代码运行次数:0
运行
复制
2020-07-25T15:10:50
应用:xxx
主机:xxx(
agentId: yyyy
发生FGC,共耗时:25012ms

2020-07-25T15:10:25
应用:xxxx
主机:xxxx
agentId: yyy
发生FGC,共耗时:4223ms

涉及到对OP系统调用的各系统都在反馈出现dubbo调用超时,都在报错中,我们通过pinpoint也发现应用频繁发生了FGC:

1.png

2.png

上面我们大概可以判断出来,是由于Metaspace元空间不足,出现内存溢出,导致jvm频繁触发full GC,为了保证业务正常,此时我们让运维紧急重启了服务器,通过重启服务器,业务逐渐恢复正常,元空间使用量也降下来了。在发生FGC时让运维dump内存了,后面会分析该文件。

JVM参数

OP应用的生成JVM参数如下:

代码语言:javascript
代码运行次数:0
运行
复制
/usr/local/java/bin/java 
-server  #指定JVM的启动模式是client模式还是server模式
-Xms4g   #初始化堆内存4G,堆内存最小值
-Xmx4g   #最大堆4g  
-Xmn2g   #年轻代2G,老年代大小=Xmx-Xmn
-Xss512k #每个线程的堆栈大小
-XX:MetaspaceSize=256m    #元空间的初始大小
-XX:MaxMetaspaceSize=512m #元空间最大值
-XX:-UseGCOverheadLimit   #预测是否要OOM了,提前抛出异常,防止OOM发生
-XX:+DisableExplicitGC    #禁用System.gc()
-XX:+UseConcMarkSweepGC   #指定老年代的收集算法使用CMS,会默认使用ParNew作为新生代收集器
-XX:+CMSParallelRemarkEnabled  #开启并行标记,减少停顿时间
-XX:+UseCMSCompactAtFullCollection  #FULL GC时对老年代进行压缩。CMS默认不会移动内存,因此容易产生碎片。增加该参数虽然会影响性能,但可以消除碎片
-XX:+UseFastAccessorMethods  #正确获取方法的调用计数,以便VM可以更好地识别代码中的热点
-XX:+UseCMSInitiatingOccupancyOnly  #指定HotSpot VM总是使用-XX:CMSInitiatingOccupancyFraction的值作为老年代使用率限制来启动CMS垃圾回收。如果没有使用-XX:+UseCMSInitiatingOccupancyOnly,那么HotSpot VM只是利用CMSInitiatingOccupancyFraction启s动第一次CMS垃圾回收,后面都是使用HotSpot VM自动计算出来的值
-XX:CMSInitiatingOccupancyFraction=70  #CMS垃圾收集器,老年代使用率达到70%时,触发CMS垃圾回收
-XX:LargePageSizeInBytes=128m  #堆内存大页的大小,大的内存分页可以增强 CPU 的内存寻址能力,从而提升系统的性能
-Djava.awt.headless=true 
-Djava.net.preferIPv4Stack=true 
-Ddubbo.application.qos.port=12881 
-javaagent:/usr/local/pinpoint/pinpoint-bootstrap-1.6.0.jar 
-Dpinpoint.agentId=driver-op-...
-Dpinpoint.applicationName=OP
-Djava.ext.dirs=/usr/local/springboot/OP/lib:/usr/local/java/jre/lib/ext 
-XX:+HeapDumpOnOutOfMemoryError  #当堆内存空间溢出时输出堆的内存快照,配合-XX:HeapDumpPath使用
-XX:HeapDumpPath=/home/admin     #当堆内存空间溢出时输出堆的内存快照输出目录
-cp /usr/local/springboot/OP/conf:.:/usr/local/java/lib:/usr/local/java/jre/lib -jar /usr/local/springboot/OP/OP.jar

由配置的JVM参数知道,指定了CMS为老年代的垃圾收集器,默认ParNew为新生代垃圾收集器,最大堆4g,老年代2g,年轻代2g,年轻中Eden区域和Survivor区域(From幸存区或To幸存区)的默认比例为8, 即设置survivor:eden=2:8(From:TO:eden=200MB:200MB:1600MB),元空间初始化大小256MB,最大值512MB,如果老年代空间使用率达到70%,会触发CMS垃圾回收。由pinpoint上可以看出,元空间使用大概在770MB左右,超过了最大元空间值,导致元空间内存不足,触发FGC,这里有个疑问,明明配置的最大512MB,为什么使用了770MB,Metaspace还有一个区间是Klass Metaspace,由参数-XX:CompressedClassSpaceSize进行控制,JDK8的时候 Klass Metaspace默认是1G。

原因分析

  • MAT分析 使用MAT打开dump文件,点开Histogram柱状图,选择java.lang.Class,右击选择List objects,选择with incoming references(当前查看的对象,被外部引用),查看通过这个class创建的类信息:

发现创建了大量Proxy类,右击选中Path To GC Roots,选中exclude all phantom/weak/soft etc.references(排除虚引用/弱引用/软引用等的引用链,被虚引用/弱引用/软引用的对象可以直接被GC给回收,要看该对象否还存在Strong引用链,如果有,则说明存在内存泄漏):

发现Proxy类被org.springframework.boot.loader.LaunchedURLClassLoader强引用,导致生成的Proxy类无法被卸载一直残留在MetaSpace区造成内存泄漏。

  • 代码分析 上面分析出来生成Proxy类可能存在内存泄漏,代码中会发现用动态代理创建Proxy类对象并放入WeakReference中,每次GC时该对象都会被回收,会重复创建Proxy类对象,而且类加载器不会被回收,导致类不会被卸载。具体代码参考了dubbo代码com.alibaba.dubbo.common.bytecode.Proxy#getProxy(java.lang.ClassLoader, java.lang.Class<?>...)
  • 解决方法 上层业务做缓存处理,不会重复创建Proxy对象。上线观察优化前后5天内的元空间增长,的确效果比较明显。

参考文章

  • https://www.jianshu.com/p/738b4f3bc44b
  • https://www.cnblogs.com/throwable/p/12216546.html
  • https://blog.csdn.net/a15939557197/article/details/90635460?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端沉思录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
线上服务的FGC问题排查,看这篇就够了!
线上服务的GC问题,是Java程序非常典型的一类问题,非常考验工程师排查问题的能力。同时,几乎是面试必考题,但是能真正答好此题的人并不多,要么原理没吃透,要么缺乏实战经验。
猿天地
2020/06/10
9520
线上服务的FGC问题排查,看这篇就够了!
JVM堆内存导致的FGC问题排查
Survivor Memory spaces (S0, S1): 幸存区,发生minor gc时,幸存区的对象全部复制到另一个里面去。
凯哥的Java技术活
2022/12/18
1.2K0
JVM堆内存导致的FGC问题排查
JVM实战—9.线上FGC的几种案例
1.如何优化每秒十万QPS的社交APP的JVM性能(增加S区大小 + 优化内存碎片)
东阳马生架构
2025/03/18
860
线上服务的FGC问题排查,看这篇就够了!
整个案例的分析过程中,其实涉及到很多GC的原理知识,如果不懂得这些原理就着手处理,其实整个排查过程是很抓瞎的。
码农架构
2020/11/09
1.9K0
线上服务的FGC问题排查,看这篇就够了!
GC原理介绍、排查FGC及线上故障的步骤
JAVA堆分为新生代(Young Generation)和老年代(Old Generation)( 也就是图中对应的New Generation 和 tenured Generation)用于存储对象实例。
chenchenchen
2022/03/09
4.7K0
GC原理介绍、排查FGC及线上故障的步骤
每天100w次登陆请求, 8G 内存该如何设置JVM参数?
上周知识星球的同学在阿里云技术面终面的时候被问到这么一个问题:假设一个每天100w次登陆请求的平台,一个服务节点 8G 内存,该如何设置JVM参数? 觉得回答的不太理想,过来找我复盘。
码猿技术专栏
2024/01/01
8060
每天100w次登陆请求, 8G 内存该如何设置JVM参数?
JVM进阶调优系列(6)一文详解JVM参数与大厂实战调优模板推荐
不管是刚入行没多久的JAVA初级研发还是10年资深架构师,手上都需要珍藏一份JVM参数指南。不仅是为了学习JVM调优,也是时刻方便在实际工作中对业务系统的调优监控做出及时调整。本文全面整理JVM参数清单,并做成表格,以及公开一份大厂生产环境核心常用的调优模板供大家参考应用,希望对有缘刷到的同学有所帮助。
拉丁解牛说技术
2024/10/24
4640
Java面试——JVM知识
【1】线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常。递归的调用一个简单的方法,不断累积就会抛出 StackOverflowError 异常。 【2】如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出 OutOfMemoryError 异常。无限循环的创建线程,并对每个线程增加内存。则会抛出 OutOfMemoryError 异常。 【注意】:在多线程的情况下,给每个线程的栈分配的内存越大,越容易产生内存溢出异常。操作系统为每个进程分配的内存是有限制的,虚拟机提供了参数来控制 Java堆和方法区这两部分共享内存的最大值,忽略程序计数器的内存消耗(很小),以及进程本身消耗的内存,剩下的内存便给了虚拟机栈和本地方法栈。每个线程分配到的栈容量越大,可以建立的线程数量自然就越少。因此,如果是建立过多的线程导致的内存溢出,在不能减少线程数的情况下,就只能通过减少最大堆和每个线程的栈容量来换取更多的线程。结合下图理解学习:
Java架构师必看
2021/05/14
5570
Java面试——JVM知识
java fgc_java Metaspace频繁FGC问题定位
数据服务是通过SQL对外提供数据查询的服务平台,底层存储支持HBase和MySQL两种。用户首先在管理平台上配置好接口的SQL详情
全栈程序员站长
2022/09/06
7060
JVM堆外内存问题排查
JVM 堆内存一般分析的比较多,本篇谈谈堆外内存问题排查,通常我们需要排查堆外内存的原因是系统整个内存使用飙高,但是堆内内存使用正常。这时候就需要分析堆外内存了
方丈的寺院
2019/08/05
5.8K0
Java堆内存又溢出了!看大师如何防范
JAVA堆内存管理是影响性能主要因素之一。 堆内存溢出是JAVA项目非常常见的故障,在解决该问题之前,必须先了解下JAVA堆内存是怎么工作的。
慕容千语
2019/06/11
1.5K0
记录线上服务频繁full gc问题排查
线上服务GC问题,是JAVAJAVA程序比较典型的问题,也是非常考验工程师的排查能力。能真正排查定位的人不多,要么原理没吃透、要么没有实战经验,看到此问题无从下手。
姆斯java实战分享
2023/07/23
1.5K0
JVM内存管理
Java内存管理是一项持续的挑战,同时也是锻造出可拓展应用的必备技能。本质上,Java内存管理就是一个为新对象分配内存和释放无用对象内存的过程。
码代码的陈同学
2018/06/03
2.3K0
JVM内存管理
18.jvm调优工具及案例分析
因为我的是mac电脑,所以运行程序都是在mac上,有时一些工具在mac上不是很好用。如果有不好用的情况,可以参考文章:
用户7798898
2021/11/19
7510
Java堆内存设置
Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。
码客说
2020/05/09
3.4K0
java(9)-深入浅出GC垃圾回收机制
1、本文了解GC垃圾回收机制,深入理解GC后才明白,为啥FGC会导致stop-the-world。 2、了解GC算法。
黄规速
2022/04/14
1K0
java(9)-深入浅出GC垃圾回收机制
【RocketMq】NameServ启动脚本分析(Ver4.9.4)
最开始的mqnamesrv.sh脚本获取环境变量的部分看不懂其实没啥影响,大略有个印象即可,当然可以截取部分的命令到Linux运行测试一下就明白了,比如准备环境变量等等,最后一句话比较关键。
阿东
2023/02/22
6040
谈JVM线程和内存参数合理性设置
在今年的敏捷团队建设中,我通过Suite执行器实现了一键自动化单元测试。Juint除了Suite执行器还有哪些执行器呢?由此我的Runner探索之旅开始了!
京东技术
2023/08/25
1.9K0
谈JVM线程和内存参数合理性设置
JVM内存分配与回收
大多数情况下,对象在新生代中 Eden 区分配。当 Eden 区没有足够空间进行分配时,虚拟机将发起一次Minor GC。我们来进行实际测试一下。
爱撸猫的杰
2020/03/12
1.6K0
JVM实战—10.MAT的使用和JVM优化总结
2.百万级数据误处理导致频繁FGC(大数据量加载到内存处理 + String.split())
东阳马生架构
2025/03/19
1470
相关推荐
线上服务的FGC问题排查,看这篇就够了!
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验