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

一次完整的JVM堆外内存泄漏故障排查记录 记录一次线上JVM堆外内存泄漏问题的排查过程与思路,其中夹带一些「JVM内存分配的原理分析」以及「常用的JVM问题排查手段和工具分享」,希望对大家有所帮助。在整个排查过程中,我也走了不少弯路,但是在文章中我仍然会把完整的思路和想法写出来,当做一次经验教训,给后人参考,文章最后也总结了下内存泄漏问题快速排查的几个原则。「本文的主要内容:」故障描述和排查过程故障原因和解决方案分析JVM堆内内存和堆外内存分配原理常用的进程内存泄漏排查指令和工具介绍和使用。 阅读详情

问题发现

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

我使用的版本是 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)

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

整体结构表示如下 ​

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

简单理解,方法区是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/。

这边看到了很多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,这个会将类加载全部打印出来。

如下图:

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

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

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

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

代码修改

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

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

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

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

MVEL.executeExpression()
MVEL.compileExpression()

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

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

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

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

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

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

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

总结

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

JVM堆外内存问题排查 摘要 JVM 堆内存一般分析的比较多,本篇谈谈堆外内存问题排查,通常我们需要排查堆外内存的原因是系统整个内存使用飙高,但是堆内内存使用正常。这时候就需要分析堆外内存了 堆外内存组成 通常JVM的参数我们会配置 -Xms 堆初始内存 -Xmx 堆最大内存 -XX:+UseG1GC/CMS 垃圾回收器 -XX:+DisableExplicitGC 禁止显示GC -XX:MaxDirec... 阅读详情

相关推荐

jvm排查问题-实践追踪问题 与思路--堆内堆外内存泄漏排查方针

JDK默认使用G1GC算法,G1将堆内存划分为等大的Region,并逐一选取可用的Region分配对象。一个对象的Size超过Region大小的一半则被称为大对象(Humongous),G1会将大对象独立放在一个或连续多个Region中。独立分配/回收大对象的成本比较低,不需要和其它小对象公用Region;存放大对象的Region放在独立的区域中,存活的大对象不会被GC Copy,介绍了GC开销;

凌波漫步 1570

JVM参数MetaspaceSize的误解

前言 昨天谢照东大神在群里提出一个问题:怎么查看Metaspace里具体包含的是什么,起因是他的某个服务设置了-XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m,但是通过jstat -gcutil pid查看M的值为98(M的=MU/MC),即Metaspace区的使用量达到了512m*98%。遗憾的是,这个推算是错误的; 推理 以笔者测试环境上...

varyall的专栏 2871

一次完整的JVM NativeMemoryTracking 堆外内存泄露分析

每年转战互联网行业的人很多,说白了也是冲着高薪去的,不管你是即将步入这个行业还是想转行,学习是必不可少的。作为一个Java开发,学习成了日常生活的一部分,不学习你就会被这个行业淘汰,这也是这个行业残酷的现实。如果你对Java感兴趣,想要转行改变自己,那就要趁着机遇行动起来。或许,这份限量版的Java零基础宝典能够对你有所帮助。《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!兴趣,想要转行改变自己,那就要趁着机遇行动起来。或许,这份。

2401_84412988的博客 1381

JVM-内存溢出

可能发生内存溢出的区域 Metaspace区域 栈内存 堆内存 一.Metaspace区域 在jvm参数配置中通过以下参数分配限制大小 -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m Metaspance区域内存溢出的主要两个原因: 第一种原因,上线系统Metaspace区域直接用默认的参数,即根本不设置其大小。这会导致默认的Metaspace区域可能才几十MB而已,此时对于一个稍微大型一点的系统,因为他自己有很多类,还依赖了很多..

刘骐毓的博客 512

JVM堆外内存与元空间OOM问题排查实战

JVM内存管理是Java性能优化的核心领域,其中非堆内存(包括元空间和堆外内存)的OOM问题往往比堆内存溢出更难排查。元空间存储类元数据等JVM运行时信息,而堆外内存则通过DirectByteBuffer等技术实现高性能IO操作。当这些区域发生内存泄漏时,常规的堆分析工具往往失效,需要借助Native Memory Tracking等特殊工具。通过监控内存分配、分析类加载情况、检查资源释放逻辑,可以定位到Netty未释放ByteBuf或Groovy动态类加载等典型问题。本文结合金融系统和微服务案例,详解如何

weixin_34341117的博客 425

Cisco思科 电子科技大学计算机网络 SRWE 期末技能考试 (PTSA)

电子科技大学2024春_SRWE_计算机网络系统 SRWE 期末技能考试 (PTSA)

2301_76745065的博客 5098

JVM Metaspace内存溢出问题

METASPACE 内存溢出问题

程序猿进阶 2844

基于32nm CMOS工艺的传输门D触发器设计及HSPICE仿真分析

根据半导体集成电路、利用Hspice软件以及数字电路等课程的知识,使用集成电路CMOS工艺完成触发器的设计,帮助初学者熟悉和掌握集成电路芯片电路设计及模拟方法和技巧

2302_81301620的博客 2457

初识JVM参数MetaspaceSize

前言 因线上内存溢出java.lang.OutOfMemoryError: Compressed class space问题,了解到Metaspace。问题出现的背景是将JDK 1.7升级到1.8,临时处理方案 添加配置参数-XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m。但是通过jstat -gcutil pid查看M的值为98(M的=MU/MC),即Metaspace区的使用量达到了512m*98%。遗憾的是,这个推算是错误的; 推理 以笔者测试环.

"零点起航"的专栏 2855

PyTorch实战:3D U-Net医学图像分割从零搭建(附完整代码)

本文详细介绍了如何使用PyTorch从零搭建3D U-Net模型进行医学图像分割,涵盖三维卷积实现、模块化架构设计、医学图像专属优化策略及完整训练流程。通过实战代码演示,帮助开发者掌握UNet-3D在CT、MRI等三维医学图像分割中的应用,提升肝脏肿瘤分割、肺结节检测等任务的准确性。

weixin_29061997的博客 330

搭建云平台 1 Day 虚拟机安装(超详细)

点击OFF 那个按钮看看网络的地址和速度有没有显示,如果有 则到目前为止都是成功的。Host name随便起一个名字就行了**-_-**文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**,如果我们安装的VMware14就会默认为。因为我的是VMware16,所以默认。也是可以的,但是千万不要用。

2401_84009679的博客 563

线上java JVM问题排查

作者:霞落满天 第一部分 是我以前公司的一则正式案例: 第二部分 是我另一个博客上写的主要是最近发现大家问的比较多就写了此文 第一部分 线上真实故障案例 下面是一个老系统,代码写的有点问题导致出现这样一个JVM占比过高的问题,正常情况下也就是CPU负载不高的时候21:00左右的,也有30万,但是再多一点30几万就是阈值,就会出现堆积。 这个队列一直是增长的快。 这个是...

深度Java 1万+

【知识点总结】电路原理 第一讲

大家好,从今天起,我将开始更新自上大学以来所学到的各种知识,之所以选择更新它们是因为最近我一直在收拾整理自己自上大学以来所收获到的各种知识,看着那厚厚一沓的书本心里不由得犯愁。如果我想找到特定的知识,那岂不是得翻到猴年马月。俗话说得好,下雨天打鞋子,闲着也是闲着,为此我设置一个专栏,将知识点一一分类总结,目的在于更好的整理所学的知识。本人学艺不精,有一些知识点地方可能存在瑕疵,希望各位大佬可以多多指教。以下是本篇文章正文内容虚断:一般通用性运算放大器的正反向输入端之间的电阻非常非常的大。

weixin_51130221的博客 4万+

Java堆外内存之六:堆外内存溢出问题排查

一、堆外内存组成 通常JVM的参数我们会配置 -Xms 堆初始内存 -Xmx 堆最大内存 -XX:+UseG1GC/CMS 垃圾回收器 -XX:+DisableExplicitGC 禁止显示GC -XX:MaxDirectMemorySize 设置最大堆外内存,默认是-xmx-survivor,也就是基本上和-xmx大小相等 -Xss:每个线程的堆栈大小,默认1M -Xmn: 年轻代大小(eden区+2 survivor) -XX:newRatio: 4 年轻代与老年代1:4 -XX:survivor

weixin_42073629的博客 1801

实战篇【2】-H3C防火墙开局基础配置

本篇文章为“H3C防火墙开局基础配置”,主要涵盖配置管理地址、聚合端口、安全区域、安全策略、静态路由、ACL,WEB和SSH远程管理、Console、SNMP、NTP时钟、日志主机等内容,对比交换机配置开局,增加两处配置,安全区域和安全策略。方便调试人员远程管理、调试。

weixin_47402139的博客 1万+

Android:Google三方库之Adjust集成详细步骤

Android为APP添加google adjust功能

学习就是我的个性 4477

Java堆外内存:堆外内存溢出问题排查

一、堆外内存组成 通常JVM的参数我们会配置 -Xms 堆初始内存 -Xmx 堆最大内存 -XX:+UseG1GC/CMS 垃圾回收器 -XX:+DisableExplicitGC 禁止显示GC -XX:MaxDirectMemorySize 设置最大堆外内存,默认是-xmx-survivor,也就是基本上和-xmx大小相等 -Xss:每个线程的堆栈大小,默认1M -Xmn: 年...

hellozhxy的博客 3109

国自然基金申请书撰写新思路:如何合理利用AI提高本子竞争力?

最后给大家推荐的400多所高校教师都在用的AI平台——高级学术版专业应用平台,集成了国际主流学术应用,以及ChatGPT、Claude、Gemini、Grok等Al大模型的高级版本平台,还有,可以辅助大家高效完成科研和学术写作任务,当然撰写硕博论文,课题、职称论文、国社科、国自然基金本子也是手拿把掐。创新点、技术路线、前期基础,但是我们也可以把写好的初稿交给AI,让它扮演资深研究员,帮我们精炼表述、优化段落结构,并揪出可能被评审追问的逻辑漏洞。,我们需要的是让它帮我们理清楚思路,而不是让它替我们完成内容。

智能AI让学术科研更容易 667

一次jvm导致线上内存占用过高问题定位

背景:8G物理内存,8核CPU,jvm使用的G1垃圾回收器。 问题:线上内存占用告警,内存占用超过85%,且现象一直持续。 分析 看一下jvm启动参数配置: -Xms6144m -Xmx6144m -Xss256k -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+DisableExplicitGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -XX:+PrintGCD

yzh_1346983557的博客 6533

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

java堆外内存泄漏分析排查

本文档用于总结记录在遇到可能堆外内存溢出情况下,不重启java进程,如何去判断是否有堆外内存溢出的风险,然后如何去定位风险的来源点。如果在更复杂的情况下,单凭数据的内容是不一定能查找到原因的,当在内存还在不断增长的情况下,可以尝试通过非java的方式去监控java进程向系统申请内存的情况。原理通过使用自己实现的libtcmalloc.so来替换原有的内存分配函数, 来达到监控内存分配的目的这里可以使用google的性能分析工具gperftools的heap-checker。

... 6142

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

arthas 排查内存溢出_Java堆外内存之六:堆外内存溢出问题排查

一、堆外内存组成通常JVM的参数我们会配置-Xms 堆初始内存-Xmx 堆最大内存-XX:+UseG1GC/CMS 垃圾回收器-XX:+DisableExplicitGC 禁止显示GC-XX:MaxDirectMemorySize 设置最大堆外内存,默认是-xmx-survivor,也就是基本上和-xmx大小相等-Xss:每个线程的堆栈大小,默认1M-Xmn: 年轻代大小(eden区+2 surv...

weixin_31998661的博客 5445

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

java 查看堆外内存占用_java问题之2:JVM堆外内存问题排查--删除

首先确认堆占用jmap 查看heap内存使用情况jmap -heap pid1可以查看到MetaspaceSize,CompressedClassSpaceSize,MaxMetaSizejmap和jdk版本有关系,有些jdk版本会查看不到内存信息,可以使用jstat来查看统计信息jstat 收集统计信息jstat -gc pid 10001S0C/S0U S1C/S1U EC/EU CCSC/C...

weixin_34776437的博客 2473

线上服务的FGC问题排查

线上服务的FGC问题排查,看这篇就够了!

xl465564754的专栏 173

Java应用堆外内存泄漏排查

背景 我司商城系统生产服务隔一段时间就挂掉一次,所有的机器都有这个问题,而且问题出现的越来越频繁,从最开始的半个月一次,到后来一周一次、3天一次,一直到最后的1天1次甚至2次 初步排查和猜测 1、首先获取应用pid ps -ef|grep marketing-center 2、根据pid查询java应用堆内存使用情况,以及应用进程占用系统内存情况 #查看java程序GC情况以及堆内内存使用情况 ...

NicholasJosh 3379
上一篇: SpringBoot+Nacos+Kafka实现微服务流编排
下一篇: 记一次自定义Redis分布式锁导致的生产事件时间
程序员职业指南
博客等级 码龄4年 2138粉丝 960原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值