JVM堆外内存导致的FGC问题排查

问题发现

服务在线上环境频繁的Full GC。把相关运行时数据区的监控打开,发现堆外内存一直在上升。

JVM堆外内存导致的FGC问题排查

我使用的版本是 java8,jvm厂商是orcale hotspot,垃圾回收器使用的CMS+ParNew。

我使用的jvm参数是:

-Xmx6g
-Xms6g
-XX:NewRatio=1
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly
-XX:MaxTenuringThreshold=6
-XX:+ParallelRefProcEnabled
-XX:+CMSParallelRemarkEnabled
-XX:+UseCMSCompactAtFullCollection
-XX:+heapDumpOnOutOfMemoryError
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/export/Logs/gc.log

为了明确排查方向,需要研究堆内存都具体有什么东西。于是我翻看了jvm的虚拟机规范。解读如下:

java虚拟机运行时数据区

Java虚拟机定义了程序执行期间使用的各种运行时数据区域。其中一些数据区域是在Java虚拟机启动时创建的,只有在Java虚拟机退出时才会被销毁,这部分线程共有。其他数据区域为每个线程。每线程数据区域在创建线程时创建,在线程退出时销毁,也就是线程私有。

运行时数据分为以下几个部分:

1、PC寄存器(The pc Register)

每个线程一个,以保存当前执行指令的地址。一旦执行了指令,PC寄存器将用下一条指令更新。

2、虚拟机栈( Java Virtual Machine Stacks)

每个Java虚拟机线程都有一个私有Java虚拟机堆栈,与线程同时创建。虚拟机栈存储栈帧,它保存局部变量和部分结果。

虚拟机栈可能会出现Java虚拟机将抛出StackOverflowerError。

3、堆(Heap)

Java虚拟机线程之间共享堆,堆只有一个。堆是为所有类实例和数组分配内存的运行时数据区域。这也是我们创建的对象放置的区域。是最大的,最需要调优的地方。

堆是在虚拟机启动时创建的。对象的存储由垃圾收集器回收;对象永远不会显式解除分配。

如果计算需要的堆超过了自动存储管理系统的可用堆,Java虚拟机会抛出OutOfMemoryError。

4、方法区(Method Area)

存储所有类级别的数据,包括静态变量的所有线程共享。Java虚拟机只有一个方法区。存储的有类结构,例如运行时常量池、字段和方法数据,以及方法和构造函数的代码,包括类和实例初始化以及接口初始化中使用的特殊方法。

5、运行时常量池(Run-Time Constant Pool)

运行时常量池是类文件中常量池表的每类或每接口运行时的表示形式。它包含多种常量,从编译时已知的数字文本到必须在运行时解析的方法和字段引用。运行时常量池的功能类似于传统编程语言的符号表,尽管它包含比典型符号表更广泛的数据范围。

这段是我抄的,为了保持完整性,运行时常量池其实是方法区的一部分。

6、本地方法栈(Native Method Stacks)

存储本地方法信息,线程私有。

整体结构表示如下

JVM堆外内存导致的FGC问题排查

问题:方法区和元空间有什么关系?

简单理解,方法区是java的定义,而元空间则是hotspot虚拟机在1.8及其以后的实现。在1.7之前叫永久代(永久代还包含了部分老年对象),如果使用java8的话忽略永久代就行了。

根据jvm的规范,方法区内存储的都是jvm类级别的数据,包括什么构造方法,什么常量池什么的。那什么操作会使得这方面一直在上涨呢?带着问题,一步步搞定呗。

简单尝试

首先先定死metaspce的大小,不让他动态扩容,因为元空间每次调整大小都会进行一次full gc。

jvm启动参数新增。

-XX:MetaspaceSize=512m
-XX:MaxMetaspaceSize=512m

但是发现并没有用。

是否能从堆看出些端倪?

堆外内存,没有特别好的查看方法。我决定还是把堆内存dump下来看看,看能否通过堆内存,看出一些猫腻来。

将对dump下来进行分析。

使用命令 jps 找到java进程pid,指定生成文件的path。

jmap -dump:file=/path ${pid}

dump完毕后。

借助工具进行查询 首先使用mat,官方网站:
https://www.eclipse.org/mat/。

JVM堆外内存导致的FGC问题排查

这边看到了很多Netty的PoolThreaCache。

联想到netty使用了直接内存,是否和这个有关呢?

为此查询了大量资料,找到了一个参数:
-Dio.netty.maxDirectMemory 。

这个参数大概意思是调整netty堆外内存,通过它有三个取值,无论调成什么都没办法阻止堆外内存的上涨。其实在这就有点无头苍蝇乱撞了。

确实,只有两种情况会导致netty相关的堆外内存上涨。

1、要么是netty有bug 。

2、要么是使用方法不对。

netty有bug,这个可能性就算了吧。使用的版本也不是最新的,也没有直接引用netty包,都是通过例如http-client或者rpc框架引入的netty。

使用方法不对?在http client或者rpc服务的部分代码排查了一遍,基本上都是比较简单的用法,并没有直接设置很怪的参数,或者很非常规的操作。

在这就确实在堆里面找不到有用的线索了。

找到原因

貌似确实没辙了。

随后我请教了我司的超级大佬:森哥。森哥给我要了相关权限后,上去机器一顿操作。推测可能是C2 Compiler或者什么即时编译导致的问题,因为堆外都是jvm级别的数据,常规的排查确实比较难找到线索。

听完后联想到堆外不就是方法区吗,我用的java8 hotspot虚拟机,也就是元空间了。

代码里面会有什么导致元空间上涨呢?

元空间是存储jvm级别的数据,是否有很多类加载?

带着这个猜想,找到相应的参数 -verbose:class,这个会将类加载全部打印出来。

如下图:

JVM堆外内存导致的FGC问题排查

发现有非常多的ASMAccessorImpl_,而且是不会停止,一直在加载。

厚礼蟹,这就查到了原因。

那ASM是什么,如果研究过spring,就知道在aop扩展动态生成字节码,最底层其实就是ASM生成的,其实是一个字节码编辑框架。官网:https://asm.ow2.io/。

也就是说,我的代码有一个地方一直在动态生成类字节码,加载到方法区。从而导致堆外内存一直在上涨,从而导致full gc。

代码修改

那怎么定位到是哪段代码?

这个简单,打开idea,double shift,调search everywhere。

JVM堆外内存导致的FGC问题排查

排查到是mvel这个依赖框架生成的。

关于mvel,其实是spel差不多,表达式解析引擎。在项目中,mvel的使用我们只用了两行代码。

MVEL.executeExpression()
MVEL.compileExpression()

然后我们也有把编译完的进行缓存,按道理说不会一直生成类的。因为mvel这个框架实在是相关文档太少,没人维护的感觉,抱着死马当活马医的态度,去github上提一个issue,然后自己同时接着排查。

JVM堆外内存导致的FGC问题排查

幸运的是这个框架还没死绝,还有人回复。

大概意思是说,我问为什么使用你们的mvel会导致我jvm出现oom错误(频繁的full gc),另外如果说每次编译相同的内容的话,为什么没有框架层面缓存起来。回答说是需要自己缓存的。

也就是我的代码还是缓存失效了。

找到缓存的那一行,使用的是map,用key去查找的时候, 发现用的是contains,而没有用containsKey。这就导致了永远查不到,也就导致了永远会重新编译。

JVM堆外内存导致的FGC问题排查

经过修改后,问题得以解决。

JVM堆外内存导致的FGC问题排查

一条平平的线,并且没有full gc,皆大欢喜

JVM堆外内存导致的FGC问题排查

总结

堆外内存有点难搞,难以和代码联系起来。提供一个思路:可通过-verbose:class查看类加载的情况,然后具体分析。

本文链接:https://www.dzdvip.com/34402.html 版权声明:本文内容均来源于互联网。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 395045033@qq.com,一经查实,本站将立刻删除。
(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022年7月3日 21:28
下一篇 2022年7月4日 22:22

相关推荐

  • 初识SpringBoot Starter

    什么是Starter starter 是springboot 的核心,每个starter负责实现特定的功能,使用者只需引入starter即可自动配置,无需关心框架整合带来的问题。 Starter 项目结构 src |- main |- java |- resources |- META-INF |–spring.factories pom.xml spring.factories ## Initializers ## ApplicationContextInitializer接口的作用是可以在ApplicationContext初始化之前,对Spring上下文属性进行修改,既refresh()前的一个钩子函数。 org.springframework.context.ApplicationContextInitializer ## Application Listeners ## ApplicationListener 是Spring的监听器,可以通过对Spring上下文发送消息事件(由ApplicationContext. publishEvent进行消息发送),由对应的监听器进行捕获处理。 org.springframework.context.ApplicationListener ## Auto Configuration Import Listeners ## 当Spring使用ConfigurationClassParser加载完所有@Configuration后会再一次使用AutoConfigurationImportSelector排除所有不符合条件的@Configuration,排除完后则回调所有AutoConfigurationImportListener的监听器。可相当于加载并过滤完@Configuration后的钩子回调。 org.springframework.boot.autoconfigure.AutoConfigurationImportListener # Auto Configuration Import Filters # 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration定义的所有配置类增加ImportFilter来决定是否进行配置 org.spr…

    2022年7月11日
    14
  • 一个“伪数码”迷所收藏的那些电子设备

    一个“伪数码”迷所收藏的那些电子设备,从左至右依次为Redmi Note7(4+64)Black (骁龙660) iPod Touch(3th 32GB) (处理器未知) iPod Touch(4th 8GB) (Apple A4处理器) iPhone4(8GB CH/A) (Apple A4处理器) iPhone4s(16GB ZP/A) (Apple A5处理器) Redmi 6Pro(4+64)Black (骁龙625处理器) 另外还有拍摄的这一台:一加5(8+128) (骁龙835处理器) 这七台设备是我的所有电子设备,今年就上高三了,没空折腾把玩这些了,等到明年高考完再玩吧,当初第一次把玩一加5,对于当时的我还在用红米note一代,真的羡慕不已,神似iPhone7P的外观,当时最强的安卓芯片,以至于我到现在还拿来做主力机用,而红米Note7是我爸爸退役下来的,当初小米官网买的首发,4+64,1199元,红米6Pro则是从亲戚那儿弄来的,也没啥问题,就前摄像头坏了,而8G的国行苹果4是我在小黄鱼收来的,真的血赚,4s则是在闲鱼50买的,后来自己换了电池,降级了ios6,非常丝滑好用,而两台iTouch则是我拿去学校听音乐的,因为那个有app可以看电视玩游戏,高三了,也不适合带去学校了,收心吧,好好学习,这些旧物也终将会成为我们的玩具,而岁月也给他们留下来不可磨灭的划痕(指iPod不锈钢后壳)

    2022年7月31日
    18
  • 支付宝付款码皮肤怎么更换 在哪里设置付款皮肤介绍

    支付宝付款码皮肤怎么更换?对于很多用户来说,一个非常好看的付款码,将在付款时展示出个性化的感觉,所以很多用户想要设置一个付款码皮肤,下面就给大家介绍一下。 支付宝付款码皮肤更换方法 1、打开支付宝,点击上方的【付钱/收钱】,进入到界面中; 2、在付款界面中,点击红框中的【…】打开扩展选项; 3、在弹出的选项中点击第一个【付款码换肤管理】,进入到界面中; 4、在界面中即可更换我们拥有的皮肤,也可以查看实效的皮肤。 以上就是支付宝付款码皮肤怎么更换的全部内容。

    2021年6月27日
    98
  • 解决CPU资源占用过高的问题(CPU资源占用过高怎么办)

    机器假死、鼠标有如木偶一样缓慢挪动,想调出任务管理器查看究竟,居然半天才弹出显示,而且状态栏里的CPU使用率高达100%。碰到这种情况,一般初学者都会选择重装系统来解决,然而其方法效果很好,但是它存在很多缺陷,如重装系统后,原来的系统设置必须重新设置,常用软件也得重新安装,非常麻烦。其实我们完全可以不重装系统,就可以将此问题解决。 一、开机就假死,清理自启动 一般开机就假死的情况,基本都是在用户进入Windows桌面时产生,而且其CPU使用率高达100%,出现这种问题的主要原因,是由于系统加载的启动程序过多,造成CPU工作超负荷的结果。 通常情况下,正常的Windows环境,无非在启动程序里加载,系统启动程序、杀毒启动程序、QQ启动程序,以及其他应用程序等,这些根本就消耗不了多少CPU资源。所以说碰到这种情况,多半数是因为系统身中病毒,而导致机器假死的情况出现。 解决办法:在启动计算机时,不停地敲击F8键进入到系统安全模式下,然后打开“运行”对话框,输入“msconfig”命令回车,进入系统配置实用程序界面。 然后切入至“启动”标签,将里面可疑“启动项”复选框勾去掉后,单击“确定”按钮,重起进入正常的Windows环境下,系统假死情况即可消失。 当然你也可以使用第三方软件来解决,比如利用Windows优化、超级兔子等一类优化软件,对系统无用的服务进行关闭,从而达到系统优化,CPU占用资源减少的目的。

    2021年8月29日
    21
  • 白夜追凶最终boss是谁(白夜追凶最后谁坐牢了)

    “电视剧《白夜追凶》的最终boss是施广陵。《白夜追凶》是王伟执导的中国首部硬汉派悬疑推理剧,由潘粤明、王泷正、梁缘、吕晓霖、尹姝贻等领衔主演。该剧讲述了刑侦支队队长关宏峰为了洗脱弟弟关宏宇的杀人罪名,一路破获多起案件的故事。” 白夜追凶最后谁坐牢了 悬疑推理剧《白夜追凶》讲述了刑侦支队队长关宏峰被人栽赃,成为2.13吴征灭门惨案嫌疑人,为了查找真凶,所以他嫁祸给了双胞胎弟弟关宏宇,为了尽快查清真相,两人互换身份,昼伏夜出。 最终大坏蛋叶方舟中枪而死,“关宏峰”因为2.13灭门事件入狱。剧中关宏峰养了一条宠物鱼,名字叫老虎,但是在故事的结尾,被关宏峰给吃掉了。 为什么他要在被抓捕之前把自己养了多年的宠物鱼吃掉呢?可能是他害怕自己走之后,老虎鱼没人照顾会被饿死,与其这样,还不如自己吃掉。 但是,剧中开头就有说关宏峰不喝酒,为什么在吃鱼的时候他开始喝酒了呢?网友猜测,可能吃鱼的人并不是哥哥,而是弟弟关宏宇,他想继续扮演关宏峰替他坐牢,让哥哥在外面继续追查真相。而且吃掉价值几万块的非洲肺鱼,恐怕也只有弟弟才做的出来吧。 最后坐牢的是谁?很明显是弟弟又一次顶替哥哥身份被抓,如果在隧道堵车这一场戏份中,弟弟没有再次跟哥哥互换身份,那么这场隧道逃跑的戏根本没有出现的必要。 那么本案的始作俑者吴征一家灭门惨案又是谁干的呢?首先跟关宏峰兄弟没有任何关系,他是被人栽赃陷害的,为了摆脱嫌疑于是嫁祸给弟弟,从而自己可以尽快查清真相。剧中有说到哥哥接到电话找自己的枪,被人引去案发现场,黑暗恐惧症发作被叶方舟团伙拖到案发现场的。可以确认杀吴征一家的不是叶方舟,而是他的同伙干的,因为剧中有交代叶方舟给团伙打电话问他是不是准备好了,意思就是案发现场有没有布置好。 为什么周巡最后说了一句,“十五年了,我竟然都没交下你这个朋友。”这句话有两层意思,第一层,周巡以为他面前的人是关宏峰,哥哥在知道弟弟是被陷害的情况下,没有找周巡帮忙,而是选择瞒着他自己调查,周巡才会说十五年的朋友了,你都不信任我。第二层意思,周巡已经看出来兄弟俩又一次互换身份,意思是自己跟关宏峰做了十五年的朋友,都没有发现他的弟弟竟然这么重情重义,后悔没有交下关宏宇这个朋友。 那么叶方舟的背后的老板会是谁呢?现在只能确认是在法院部门有关,因为叶方舟在一次电话中有提到:“他最近官司一大堆。”在第一季中唯一具有法院背景的只有韩彬他老爹,那么…

    2022年12月16日
    44
  • 2022年618旗舰机推荐,看完你就知道选什么了

    前言:618马上就到来临,现在已经进入预热阶段,今年的手机圈可谓是内卷圈,各个品牌在上半年已经出了十几部手机,但又有哪些值得买呢,让我们一起来看看吧 IQOONeo6 12+256 ¥3099 IQNeo6可谓是一匹黑马,搭载骁龙8gen1还有独立显示芯片Pro,这颗芯片能把骁龙8gen1的功耗和发热降至最低,并且还有叠瀑稀土、六层立体石墨散热加持,让整个夏天不再炎热。 6.62英寸也算是祖传了,从5开始一直都是这个尺寸。三星E4AMOLED,120HZ屏还支持屏下双压感,后置三摄,6400W主摄+1200W超广角+200W黑白镜头。支持NFC。在续航方面也很不错,4700mAh双电芯加上80W的闪充。 VIVO X80 8+128 ¥3499X80是4月底发部的手机,就目前的情况来看,618当天还有优惠,优惠力度应该在200-400元左右,可适当冲一冲。该机有两个版本,天玑9000和骁龙8gen1。今天就推荐天玑版本,毕竟它功耗低不烫手。满血版LPDDR5+UFS3.1还有一颗自研影像芯片V1+,拍人拍物都很美。VC液冷散热虽比不上IQOONeo6的散热但这是普遍旗舰机的标配。 三星E5屏,支持120HZ刷新率,6.78英寸大屏,后置三摄,IMX866+IMX663+IMX663。主摄5000W+1200W超广角+1200W人像镜头,前置3200W还有LED补光灯。在续航方面配备了4500mAh+80W快充,可惜不支持无线,无线只要Pro版本有。但还是值得推荐的 华为Mate 40Pro 8+256 ¥6549虽然mate40pro已经是两年前的机子了,但近期华为商城又突然上架来卖,这可把花粉高兴坏了。虽然价格居高不下,但是在是香。HarmonyOS系统是华为基于安卓自研的系统,HarmonyOS3.0马上就要来了,mate40Pro肯定是可以升级的。 该机搭载了国产芯片麒麟9000,88°超曲环幕屏,最高支持90HZ刷新率,屏幕尺寸6.76英寸,OLED分辨率2772X1344像素,后置三摄5000W主摄+2000W电影超广角+1200W长焦镜头,支持OIS光学防抖,续航方面4400mAh+66W还支持50W无线充电,还有IP68防尘防水功能。 以上三款是618值得购买的手机,价格来自拿货价仅供参考,华为Mate 40Pro是全能旗舰,可惜就是屏幕刷新率低了点,…

    2022年5月18日
    18