GC安全点导致停顿时间过长的案例
GC安全点导致停顿时间过长的案例
- 前言
- 安全点的概念
- 案例分析
- 解决方法
- 如有需要收藏的看官,顺便也用发财的小手点点赞哈,如有错漏,也欢迎各位在评论区评论!
前言
前段时间在使用G1垃圾收集时,因服务读写压力过大,于是将-XX:MaxGCPauseMillis参数调整为500ms,运行一段时间后发现垃圾收集的停顿时间经常达到3s以上,但实际垃圾收集器进行回收的动作就只占其中的几百毫秒。
MaxGCPauseMillis:最大停顿时间,默认200ms。顾名思义,G1是garbage first。垃圾优先。G1在该最大停顿时间的指定范围内优先收集收益最大(标记垃圾对象最多)的region。
大概的GC日志如下:
[Times:user=1.51 sys=0.67,real=**0.14** secs]
2025-03-07T 21:05:01.376+0800: 3448319:Total time for which application threads were stopped :2.2645818 seconds
user:进程执行用户态代码所耗费的处理器时间。
sys:进程执行和心态代码所耗费的处理器时间。
real:执行动作从开始到结束耗费的时钟时间。
前面两个是处理器时间,而最后一个是时钟时间,它们的区别是处理器时间代表的是线程占用处理器一个核心的耗时计数,而时钟时间现实世界中的时间计数,故只需要关注real这个真实感受到的时间即可,因为最终用户只关心发出请求到得到响应所花费的时间,也就是响应速度,而不太关心程序到底是用了多少个线程或者处理器来完成任务。
日志显示本次垃圾收集一共花费了0.14s,但用户线程却足足停顿了2.26秒,两者差距已经远远超出了正常的TTSP(Time To Safepoint)耗时的范畴。
安全点的概念
先理清安全点的概念,避免对于案例有模糊的地方;
安全点:在垃圾收集时,需要STW,也就是要用户线程停止,但用户线程执行时并非在代码指令流的任意位置都能够停顿下来开始垃圾收集,而是强制要求必须执行到达安全点后才能够暂停。
那用户线程如何走到最近的最近安全点呢?
有两种方式:
①抢先式中断:不需要用户线程主动去配合,在垃圾收集发生时,系统会中断所有的用户线程,如果用户线程中断的地方不在安全点上,就恢复这条线程的执行,让它在跑一会再重新中断,直到跑到最近的安全点上。现在几乎没有虚拟机实现采用抢先式中断中暂停线程响应GC事件了。
②主动式中断:当垃圾收集需要中断用户线程时,不直接对线程操作,而是设置一个安全点标志位,各个线程执行过程中会不断轮询这个标志位,一旦发现标志为真时就自己在最近的安全点上主动中断挂起。
案例分析
前言中也说明了用户线程停顿了2.26s,垃圾收集却只花费了0.14s,属实本末倒置了,因为用户线程是主动式中断,于是猜想是否发生垃圾收集时,GC线程在等待用户线程进入安全点这段时间花费了2.26s,最后进入安全点后,GC线程垃圾收集只花费了0.14s?
开始验证:
在启动项目时加入参数-XX:+PrintSafepointStatistics和-XX:PrintSafepointStatisticsCount=1去查看安全点日志,如下:
vmop [threads: total initially_running wait_to_block]
65968.203: ForceAsyncSafepoint [931 1 2]
[time: spin block sync cleanup vmop] page_trap_count
[2255 0 2255 11 0] 1
日志显示当前虚拟机(VM Operation),也就是上面的vmop是等待所有用户线程进入到安全点,但是有两个线程特别慢,导致发生长时间的自旋等待。日志中的2255毫秒自旋(spin)时间就是指由于部分线程已走到了安全点,但还有一些特别慢的线程并没有到,所以GC线程无法工作,只能空转等待。
所以怎么找出导致GC线程等待那两个线程呢?
在启动时添加-XX:+SafepointTimeout和-XX:SafepointTimeoutDelay=2000两个参数,让虚拟机在等待线程进入安全点的时间超过2000毫秒时就认为超时,这样就会输出导致问题的线程名称,得到的大概日志如下:
#SafepointSynchroinze::begin: Timeout detected;
#SafepointSynchroinze::begin: Timed out while spinning to reach safepoint.
#SafepointSynchroinze::begin: Threads which did not reach the safepoint:
#"RpcServer.listener,port=24600" #32 daemon prio=5 os_prio=0 tid=0x0000f4c14b22840 nid=0xa621 runnable [0x0000000000000000]
java .lang.Thread.State: RUNNABLE
#SafepointSynchroinze::begin: (End of list)
从日志可以看到是线程名称为“RpcServer.listener,port=24600”导致GC线程发生等待。安全点是以“是否具有让程序长时间执行的特性”为原则进行选定的,所以方法调用、循环跳转、异常跳转这些位置都可能会设置有安全点,但是HotSpot为了避免安全点过多带来过重的负担,对循环还有一项优化措施,认为循环次数较少的话,执行时间应该不会太长,所以是使用int类型或范围更小的数据类型作为索引值的循环默认是不会被防止安全点的,这种循环被称为可数循环。而使用long类型或范围更大的数据类型作为索引值的循环就被称为不可数循环,这种就会被放置安全点。
针对HotSpot有这一优化点,于是翻看RpcServer那块代码发现,很多个上游服务调本服务,有很多连接超时,最后又在清理超时连接的方法发现使用for循环清理,而且是使用int变量记录循环次数,伪代码如下:
for(int i=0;i<list.size;i++){...........socket.close();
}
这是一个可数循环,HotSpot不会在循环中插入安全点。当垃圾收集发生时,如果RpcServer的Listener线程刚好执行到这个方法里的可数循环时,就必须等待循环全部跑完才能进入安全点,此时其他线程也必须一起等待,所以看起来就是长时间的停顿。
解决方法
上面也说过,可数循环不可放置安全点,那改为不可数循环即可,也就是将int变量改为long类型即可。
如有需要收藏的看官,顺便也用发财的小手点点赞哈,如有错漏,也欢迎各位在评论区评论!
相关文章:
GC安全点导致停顿时间过长的案例
GC安全点导致停顿时间过长的案例 前言安全点的概念案例分析解决方法如有需要收藏的看官,顺便也用发财的小手点点赞哈,如有错漏,也欢迎各位在评论区评论! 前言 前段时间在使用G1垃圾收集时,因服务读写压力过大…...
linux下 jq 截取json文件信息
背景:通过‘登录名‘ 获取该对象的其他个人信息如名字。 环境准备:麒麟操作系统V10 jq安装包 jq安装包获取方式:yum install jq 或 使用附件中的rpm 或 git自行下载 https://github.com/stedolan/jq/releases/download/ 实现过程介绍&am…...
git lfs使用方法指南【在github保存100M以上大文件】
为了在 GitHub 仓库中存储超过 100MB 的大文件并避免推送失败,使用 Git LFS(Large File Storage) 是最佳解决方案。以下是详细步骤: 一、安装 Git LFS 下载并安装 Git LFS: 访问 Git LFS 官网 下载对应系统的安装包。或…...
躲藏博弈:概率论与博弈论视角下的最优策略选择
躲藏博弈:概率论与博弈论视角下的最优策略选择 1. 问题引入 想象这样一个场景:你在厕所里藏了一部手机,一周过去了,它仍未被发现。现在你面临一个决策: 选项A:继续将手机留在原处选项B:将手机…...
类加载器加载过程
今天我们就来深入了解一下Java中的类加载器以及它的加载过程。 一、什么是类加载器? 在Java中,类加载器(Class Loader)是一个非常重要的概念。它负责将类的字节码文件(.class文件)加载到Java虚拟机&#x…...
Python中dump、dumps和load、loads的异同
Python中dump、dumps和load、loads的异同 Python中dump、dumps和load、loads的异同 1. json.dump()和json.dumps() 1.1 json.dump()1.1 json.dumps() 2. json.load()和json.loads() 2.1 json.load()2.2. json.loads() 3. 总结对比4. 区分5. 完整代码 1. json.dump()和json.dum…...
Spring Boot整合ArangoDB教程
精心整理了最新的面试资料和简历模板,有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 一、环境准备 JDK 17Maven 3.8Spring Boot 3.2ArangoDB 3.11(本地安装或Docker运行) Docker启动ArangoDB docker run -d --name ar…...
vue3框架的响应式依赖追踪机制
当存在一个响应式变量于视图中发生改变时会更新当前组件的所以视图显示,但是没有视图中不写这个响应式变量就就算修改该变量也不会修改视图,这是为什么?我们能否可以理解宽泛的理解为vue组件的更新就是视图的更新,单当视图中不存在…...
软件工程:软件需求之需求分析方法
目录 前言 需求分析方法 工具和方法 具体分析方法 对运行环境的影响 编辑 前言 本文重点介绍开展软件需求分析的方法。 需求分析方法 工具和方法 软件需求可以维护在ALM系统中,譬如:doors,codeBeamer等,JIRA适合互联网行…...
【网络编程】WSAAsyncSelect 模型
十、基于I/O模型的网络开发 接着上次的博客继续分享:select模型 10.8 异步选择模型WSAAsyncSelect 10.8.1 基本概念 WSAAsyncSelect模型是Windows socket的一个异步I/O 模型,利用这个模型,应用程序 可在一个套接字上接收以Windows 消息为基…...
视觉-语言模型-出发点CLIP--(精读论文)
阅读建议:配合这个源码分析阅读效果更加 研究背景和目的 介绍当前计算机视觉系统依赖固定类别标签训练的局限性,以及自然语言监督作为一种有潜力替代方式的研究现状。强调论文旨在探索从自然语言监督中学习可迁移视觉模型,实现零样本学习&a…...
docker本地部署RagFlow
1.安装 克隆仓库 git clone https://github.com/infiniflow/ragflow.git构建预建的Docker映像并启动服务器 cd ragflow/docker chmod x ./entrypoint.sh docker compose -f docker-compose.yml -p ragflow up -d修改ragflow/docker/.env文件 #RAGFLOW_IMAGEinfiniflow/ragfl…...
机器学习数学基础:44.多元线性回归
一、文字内容详解 1. 多重共线性的判断——皮尔逊相关系数 皮尔逊相关系数用于衡量自变量间的线性相关程度,取值范围为 ([-1, 1]): 绝对值越接近 (1),变量间线性相关性越强;越接近 (0),相关性越弱。在多重共线性判断…...
GetWindowLongPtr函数分析
第一部分: #ifdef UNICODE FUNCLOG2(LOG_GENERAL, LONG_PTR, APIENTRY, GetWindowLongPtrW, HWND, hwnd, int, nIndex) #else FUNCLOG2(LOG_GENERAL, LONG_PTR, APIENTRY, GetWindowLongPtrA, HWND, hwnd, int, nIndex) #endif // UNICODE LONG_PTR APIENTRY GetWin…...
大语言模型(LLM)和嵌入模型的统一调用接口
ChatModelFactory、EmbeddingModelFactory 讲解代码:import os from dotenv import load_dotenv, find_dotenv_ load_dotenv(find_dotenv())from langchain_openai import ChatOpenAI, OpenAIEmbeddings, AzureChatOpenAI, AzureOpenAIEmbeddingsclass ChatModelF…...
大白话html语义化标签优势与应用场景
大白话html语义化标签优势与应用场景 大白话解释 语义化标签就是那些名字能让人一看就大概知道它是用来做什么的标签。以前我们经常用<div>来做各种布局,但是<div>本身没有什么实际的含义,就像一个没有名字的盒子。而语义化标签就像是有名…...
Scala:在哪里写类的属性?类的属性必须私有吗?类的必须初始化吗?
哪里写类的属性 直接在类体中定义属性 class Circle {private var _radius: Double 0.0def radius: Double _radiusdef radius_(newRadius: Double): Unit {_radius newRadius}def area: Double scala.math.Pi * _radius * _radius } 可以在类体内部直接定义属性。例如&am…...
Android源码编译命令详解
一、引言 先看下面几条指令,相信编译过Android源码的人都再熟悉不过的。 source setenv.sh lunch make -j8记得最初刚接触Android时,同事告诉我用上面的指令就可以编译Android源码,指令虽短但过几天就记不全或者忘记顺序,每次编…...
任务11:路由器配置与静态路由配置
目录 一、概念 二、路由器配置 三、配置静态路由CSDN 原创主页:不羁https://blog.csdn.net/2303_76492156?typeblog 一、概念 1、路由器的作用:通过路由表进行数据的转发。 2、交换机的作用:通过学习和识别 MAC 地址,依据 M…...
Unity之如何实现哔哩哔哩直播弹幕游戏
前言 什么是直播间互动? 当我们使用哔哩哔哩进行直播或者观看视频时,我们可以通过接入哔哩哔哩提供的 直播&互动玩法SDK,让直播和视频可以与Unity3D游戏客户端或者游戏服务器进行互动。 环境要求 Unity 2020.x或更高版本 依赖库:Newtonsoft Json Unity Package 在P…...
Python实例:PyMuPDF实现PDF翻译,英文翻译为中文,并按段落创建中文PDF
基于PyMuPDF与百度翻译的PDF翻译处理系统开发:中文乱码解决方案与自动化排版实践 一 、功能预览:将英文翻译为中文后创建的PDF 二、完整代码 from reportlab.lib.pagesizes import letter from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle...
LeeCode题库第四十六题
46.全排列 项目场景: 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1: 输入:nums [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]示例 2&am…...
LangChain4j开发RAG入门示例
本文将详细介绍如何基于Java语言,使用Langchain4j开源框架、Milvus向量数据、阿里Qwen大模型,开发一个RAG入门级简单示例。本示例虽然简单,但涉及到多个知识点,包括:Milvus初始化、Embedding模型、文档切片、Springboo…...
快速从C过度C++(一):namespace,C++的输入和输出,缺省参数,函数重载
📝前言: 本文章适合有一定C语言编程基础的读者浏览,主要介绍从C语言到C过度,我们首先要掌握的一些基础知识,以便于我们快速进入C的学习,为后面的学习打下基础。 这篇文章的主要内容有: 1&#x…...
课程《Deep Learning Specialization》
在coursera上,Deep Learning Specialization 课程内容如下图所示:...
微服务与消息队列RabbitMQ
简介 同步模式 异步模式 内容 解决方案RabbitMQ 同步调用的优缺点 同步调用的优势是什么? 时效性强,等待到结果后才返回。 同步调用的问题是什么? 拓展性差性能下降级联失败问题...
苹果 M3 Ultra 芯片深度解析:AI 时代的性能革命
2025 年 3 月 5 日,苹果正式发布了其史上最强 PC 芯片 ——M3 Ultra。这款基于 UltraFusion 封装技术的旗舰级 SoC,不仅延续了苹果芯片在能效比上的传统优势,更通过架构创新与硬件升级,将 AI 计算能力推向了新高度。本文将从性能突…...
通义千问:Qwen2.5-0.5B模型架构解释
通义千问:Qwen2.5-0.5B模型架构解释 1. 模型权重文件 .mdl、.msc:存储模型核心参数,是模型训练后学习到的知识载体,包含神经网络各层权重,加载后模型才能执行推理、生成等任务。 .mdl文件:通常是模型的核心权重数据文件,存储神经网络各层的权重参数、张量等关键数据,是…...
安装完flash-attn,使用时报错undefined symbol
去flash attention官网下载安装包, 注意需要根据自己的torch版本,cuda版本(可以选择低于自己cuda版本的) 和python版本进行选择。 如果whl文件名上包含参数abiTRUE,则会报错。需要安装包含abiFALSE的whl文件。 卸载:卸载原先报错的flash-attn pip uni…...
【Linux】冯诺依曼体系与操作系统理解
🌟🌟作者主页:ephemerals__ 🌟🌟所属专栏:Linux 目录 前言 一、冯诺依曼体系结构 二、操作系统 1. 操作系统的概念 2. 操作系统存在的意义 3. 操作系统的管理方式 4. 补充:理解系统调用…...
