JVM源码剖析之Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded异常
写在前面:
版本信息:
jdk版本:jdk8u40
垃圾回收器:ParallelScavenge new/old
最近在群里看到有一位老哥拿着异常信息到处问,而发生的就是java.lang.OutOfMemoryError: GC overhead limit exceeded异常,恰好看到经常有人询问关于这个异常的问题,如何发生的,要如何解决呢?所以促使我写下这篇文章,此文章分为3大块,出现的原因,如何解决,源码论证。
异常出现的原因
full gc回收时间大于98%(这里是一个算法,可以忽略,只需要明白最近一直花大量时间在GC),并且回收后可用空间小于总空间的2%。 这样的情况下,达到5次就会抛出java.lang.OutOfMemoryError: GC overhead limit exceeded异常。
总而言之:我们首先要明白,GC的过程是需要STW(STOP-THE-WORD) 也即业务线程是需要停止工作,而GC过程中消耗大量时间回收空间,而回收后的可使用空间仅仅只有总空间的2%,往往下次new对象的时候又去GC了,周而复始,给用户的体验是当前系统已经完全卡死了~
所以在种种因素下JVM认为你已经没必要去GC了,GC也是毫无意义的事情了,完全卡死的情况下,还不如我给你抛出java.lang.OutOfMemoryError: GC overhead limit exceeded异常,开发者好好去排查一下问题~
如何解决
仅供参考,还是需要分析自身系统环境做出不同的策略。
- 堆空间是否设置的太少?可以在启动时添加-Xms -Xmx参数设置堆大小
- 分析是否存在内存泄露?
- 如果项目庞大,是否需要提升硬件?
- 启动时添加-XX:+HeapDump0n0ut0fMemoryError -XX:HeapDumpPath= "路径" 参数下次发生OOM时便可分析
- 分析项目中经常使用的大对象,是否可以优化一下空间?
- 是否可以把项目中非重要的缓存数据设置成软、弱引用对象
- 以上的分析可以使用阿里开源的 "Arthas" 工具
- 还有很多笔者暂时没有考虑到的.......
源码论证
由于大部分的公司还是停留在Java8,并且垃圾回收器也是默认的ParallelScavenge new/old,所以直接给出ParallelScavenge new/old垃圾回收器的部分核心源码
src/share/vm/gc_interface/collectedHeap.inline.cpp 文件中, common_mem_allocate_noinit方法,此方法尝试开辟对象,如果开辟不成功,就会根据当前不同OOM异常种类Dump和抛出对应的异常
HeapWord* CollectedHeap::common_mem_allocate_noinit(KlassHandle klass, size_t size, TRAPS) {bool gc_overhead_limit_was_exceeded = false;// 尝试在堆空间开辟对象result = Universe::heap()->mem_allocate(size,&gc_overhead_limit_was_exceeded);// 根据gc_overhead_limit_was_exceeded参数区分是那种OOM异常。if (!gc_overhead_limit_was_exceeded) {// -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support// 英文注释特别明显了,如果设置了dump参数,就导出report_java_out_of_memory("Java heap space");// 抛出OOM:Java heap spaceTHROW_OOP_0(Universe::out_of_memory_error_java_heap());} else { // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support// 英文注释特别明显了,如果设置了dump参数,就导出report_java_out_of_memory("GC overhead limit exceeded");// 抛出OOM:GC overhead limit exceededTHROW_OOP_0(Universe::out_of_memory_error_gc_overhead_limit());}
}
而我们关心的是GC overhead limit exceeded异常,而这里是根据gc_overhead_limit_was_exceeded变量来做区分,而gc_overhead_limit_was_exceeded变量传入mem_allocate方法,所以我们接着看mem_allocate方法src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp 文件中mem_allocate方法,此方法也是开辟对象的过程。
HeapWord* ParallelScavengeHeap::mem_allocate(size_t size,bool* gc_overhead_limit_was_exceeded) {// 尝试在young_gen空间创建对象HeapWord* result = young_gen()->allocate(size);// 在年轻代没有创建出对象。while (result == NULL) {{// 因为存在锁的原因,所以下面又在年轻代尝试了一次。MutexLocker ml(Heap_lock);gc_count = Universe::heap()->total_collections();result = young_gen()->allocate(size);if (result != NULL) {return result;}// 年轻代开辟不了,老年代尝试一下。result = mem_allocate_old_gen(size);if (result != NULL) {return result;}// 超过允许尝试的次数,直接返回if (gclocker_stalled_count > GCLockerRetryAllocationCount) {return NULL;}}// 需要触发GC,回收空间后再尝试开辟对象if (result == NULL) {// 触发GC回收空间VM_ParallelGCFailedAllocation op(size, gc_count);VMThread::execute(&op);if (op.prologue_succeeded()) {// 是否达到次数,是否清空软引用了const bool limit_exceeded = size_policy()->gc_overhead_limit_exceeded();const bool softrefs_clear = collector_policy()->all_soft_refs_clear();if (limit_exceeded && softrefs_clear) {// 设置为true,代表抛出GC overhead limit exceeded异常*gc_overhead_limit_was_exceeded = true; size_policy()->set_gc_overhead_limit_exceeded(false);return NULL;}return op.result();}}}return result;
}
我们看到后续GC回收后,判断limit_exceeded 和 softrefs_clear,如果都为true就把gc_overhead_limit_was_exceeded设置为true。
而softrefs_clear变量是清空软引用,我们知道,在JVM中,内存实在不足的时候会清空软引用
而我们看到limit_exceeded变量的设置即可。看何时把他设置为true即可。
src/share/vm/gc_implementation/share/adaptiveSizePolicy.cpp 文件中check_gc_overhead_limit方法
void AdaptiveSizePolicy::check_gc_overhead_limit(size_t young_live,size_t eden_live,size_t max_old_gen_size,size_t max_eden_size,bool is_full_gc,GCCause::Cause gc_cause,CollectorPolicy* collector_policy) {// 当前eden空闲大小const size_t free_in_eden = max_eden_size > live_in_eden ?max_eden_size - live_in_eden : 0;// 当前老年代空闲大小const size_t free_in_old_gen = (size_t)(max_old_gen_size - avg_old_live()->average());// 当前堆空间空闲大小const size_t total_free_limit = free_in_old_gen + free_in_eden;// 堆空间的总大小const size_t total_mem = max_old_gen_size + max_eden_size;// GCHeapFreeLimit是2// 所以这里是算出比例,2%const double mem_free_limit = total_mem * (GCHeapFreeLimit/100.0);const double mem_free_old_limit = max_old_gen_size * (GCHeapFreeLimit/100.0);const double mem_free_eden_limit = max_eden_size * (GCHeapFreeLimit/100.0);// GCTimeLimit是98// 所以这里是算出比例,98%const double gc_cost_limit = GCTimeLimit/100.0;// 如果是fullgcif (is_full_gc) {// 如果GC时长超过98%// 并且回收后可用空间小于总空间的2%if (gc_cost() > gc_cost_limit &&free_in_old_gen < (size_t) mem_free_old_limit &&free_in_eden < (size_t) mem_free_eden_limit) {inc_gc_overhead_limit_count(); // 自增一次// 如果开启了次数限制if (UseGCOverheadLimit) {// 如果次数大于等于5次。if (gc_overhead_limit_count() >=AdaptiveSizePolicyGCTimeLimitThreshold){// 设置为true,到时候就会抛出GC overhead limit exceeded异常// 并且清空次数set_gc_overhead_limit_exceeded(true); reset_gc_overhead_limit_count();} }}}// 如果设置了UseGCOverheadLimit的情况下, 不会响应此异常if (UseGCOverheadLimit && PrintGCDetails && Verbose) {if (gc_overhead_limit_exceeded()) {reset_gc_overhead_limit_count();} }
}
这里算出了堆总空间的百分之2,gc回收时间的百分之98。然后算出了GC后空闲空间的占比,GC的回收时间。最后通过比较,如果GC回收时间大于98%,并且回收后可用空间小于总空间的2% 情况下计数器+1,如果计数器达到5次就通过set_gc_overhead_limit_exceeded方法设置为true,最终抛出java.lang.OutOfMemoryError: GC overhead limit exceeded异常。
所以看了源码后,解决GC overhead limit exceeded异常,还可以通过设置
-XX:-UseGCOverheadLimit 或者 -XX:AdaptiveSizePolicyGCTimeLimitThreshold = "设置很大的数值" 都能让JVM不抛出 GC overhead limit exceeded异常。但是没任何意义,因为会一直触发GC,一直STW暂停,用户一直是卡死的状态~
相关文章:
JVM源码剖析之Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded异常
写在前面: 版本信息: jdk版本:jdk8u40 垃圾回收器:ParallelScavenge new/old最近在群里看到有一位老哥拿着异常信息到处问,而发生的就是java.lang.OutOfMemoryError: GC overhead limit exceeded异常,恰好…...
使用PDF文件入侵任何操作系统
提示:我们8月28号开学,所以我得快点更新了,不能拖了😥 文章目录 前言一、打开终端总结 前言 PDF文件被广泛应用于共享信息,电子邮件,网站或文档或存储系统的真实链接 它可以用于恶意软件的载体。 不要问我什么意思&am…...
强训第32
选择 D B A A 发送TCP意思应该是已经建立了连接,会超时重传。在未建立连接的时候,会放弃该链接 C A 80端口是http A 交换机攻击主要有五种:VLAN跳跃攻击 生成树攻击 MAC表洪水攻击 ARP攻击 VTP攻击 B A 2^(32-26)2^(32-27)2^(32-27)128 减去…...
vue3 setup+Taro3 调用原生小程序自定义年月日时分多列选择器,NutUI改造
vue3 setupTaro3 调用原生小程序自定义年月日时分多列选择器,NutUI改造 NutUI 有日期时间选择器,但是滑动效果太差,卡顿明显。换成 原生小程序 很顺畅 上代码: <template><view><pickermode"multiSelector&…...
git命令使用
君子拙于不知己,而信于知己。——司马迁 清屏:clear 查看当前面板的路径:pwd 查看当前面板的文件:ls 创建文件夹:mkdir 文件夹名 创建文件:touch 文件名 删除文件夹:rm -rf 文件夹名 删除文件:r…...
每日记--前端解决方案--el-select下拉样式-el-option内容过长-鼠标悬停到文字不修改光标样式-设置透明
文章目录 el-select下拉样式el-select中el-option内容过长解决办法鼠标悬停到文字不修改光标样式设置透明 el-select下拉样式 element-ui自带样式设置popper-class el-select中el-option内容过长解决办法 问题:像这样选项太长了,不好看 解决…...
Windows系统Git安装教程(详细Git安装过程)
获取Git安装程序 到Git官网下载,网站地址:https://git-scm.com/downloads,如下图: 因为我们是用Windows系统上的浏览器访问的,Git官网自动之别到了我使用的操作系统,所以右侧直接显示下载使用Windows系统的…...
前后端分离------后端创建笔记(11)用户删除
B站视频:30-用户删除&结束语_哔哩哔哩_bilibili 1、现在我们要做一个删除的功能 1.1 首先做一个删除的功能接口,第一步先来到后端,做一个删除的接口 2、删除我们用Delete请求 3、方法名我给他改一下 3.1这里给他调一下删除方法…...
24、springboot的自动配置01--类条件注解@ConditionalOnClass、bean条件注解@ConditionalOnBean
springboot的自动配置 ★ 自动配置 Spring Boot的自动配置通常可根据依赖库自动触发——当Spring Boot检测到项目中包含某些框架的JAR包时,Spring Boot就会触发自动配置。其实通过EnableAutoConfiguration注解来启动▲ 其实你用到SpringBootApplication࿰…...
婚恋交友h5多端小程序开源版开发
婚恋交友h5多端小程序开源版开发 以下是婚恋交友H5多端小程序的功能列表: 用户注册和登录:用户可以通过手机号码或第三方账号注册和登录。个人信息填写:用户可以填写个人基本信息,包括姓名、性别、年龄、身高、体重、学历、职业等…...
uniapp案例30余种实战项目
uniapp案例30余种实战项目 mpvue框架仿滴滴出行didi-masteruni-app自定义导航栏title-customvue-mpvue-ChatRobot聊天机器人vue-mpvue-ChatRobot-master一款播课类小程序, 基于 mpvue 构建mp-podcast-mpvue-mastermpVue高仿美团小程序教程mpvue-meituan-masteruni-app 二维码生…...
回归预测 | MATLAB实现GRNN广义回归神经网络多输入多输出预测
回归预测 | MATLAB实现GRNN广义回归神经网络多输入多输出预测 目录 回归预测 | MATLAB实现GRNN广义回归神经网络多输入多输出预测预测效果基本介绍程序设计往期精彩参考资料 预测效果 基本介绍 MATLAB实现GRNN广义回归神经网络多输入多输出预测,输入10个特征&#x…...
从零开始学习VBA(一)
前置配置设置 首先配置开发设置(不同版本的配置方法会有差异,可根据自己使用的EXCEL版本到网络上找对应的方法,比如直接搜索Excel2010 开发工具) 以下为excel2016配置方法: 操作路径:文件-选项-自定义功…...
Kotlin Executors线程池newSingleThreadExecutor单线程
Kotlin Executors线程池newSingleThreadExecutor单线程 import java.util.concurrent.Executorsfun main() {val mExecutorService Executors.newSingleThreadExecutor()for (i in 1..5) {mExecutorService.execute {println("seq-$i tid:${Thread.currentThread().threa…...
ZooKeeper介绍
ZooKeeper是一个开放源代码的分布式协调服务。ZooKeeper的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。 ZooKeeper是一个典型的分布式数据一致性的解决方案࿰…...
首起针对国内金融企业的开源组件投毒攻击事件
简述 2023年8月9日,墨菲监控到用户名为 snugglejack_org (邮件地址:SnuggleBearrxxhotmail.com)的用户发布到 NPM 仓库中的 ws-paso-jssdk 组件包具有发向 https://ql.rustdesk[.]net 的可疑流量,经过确认该组件包携带远控脚本&a…...
源于传承,擎领未来,新架构、新工艺下的“换心工程”——金融电子化访中电金信副总经理、研究院院长况文川
当前,商业银行的经营环境正在发生着深刻而复杂的变化,在深化改革主旋律的指引下,数字化转型已成为我国商业银行普遍认同、广泛采用的战略性举措。核心系统作为承载银行业务的关键支柱系统,一直是各银行在金融科技建设中重点关注和…...
pytest数据驱动(最简单)
目录 第一种:通过yaml文件获取数据(一维列表) 第二种:通过yaml文件获取数据(二维列表) 第三种:通过yaml文件获取数据(pytest.fixture) 资料获取方法 第一种ÿ…...
分布式 - 服务器Nginx:一小时入门系列之代理缓冲与缓存
官方文档:https://nginx.org/en/docs/http/ngx_http_proxy_module.html 1. 代理缓冲 proxy_buffer 代理缓冲用于临时存储从后端服务器返回的响应数据。通过使用代理缓冲,Nginx可以在接收完整的响应后再将其发送给客户端,从而提高性能和效率…...
什么是集成学习算法
目录 什么是集成学习算法 集成学习发展史 集成学习组织方式 1) 并联组织关系...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
