JVM——HotSpot的算法细节实现

一、根节点枚举

        固定可作为GC Roots的节点主要在全局性的引用(如常量或类静态属性)与执行上下文(如栈帧中的本地变量表)中,尽管目标明确,但查找要做到高效很难。现在java应用越来越庞大,光方法区的大小就常有数百上千兆,里面的类、常量等更是恒河沙数,逐个检查以这里为起源的引用肯定得消耗不少时间。

        同时迄今为止,所有收集器在根节点枚举这一步时都是必须暂停用户线程的。根节点枚举必须在一个保障一致性的快照中进行。一致性的意思是整个枚举期间执行子系统看起来就像被冻结在某一个时间点上,不会出现分析过程中,根节点集合的对象引用关系还在不断的变化的情况,若这点不能满足,分析结果准确性也就无法保证

        由于目前主流java虚拟机使用的都是准确式垃圾收集(准确式内存管理:虚拟机可以知道内存中某个位置的数据具体是什么类型),所以当用户线程停顿时,不需要一个不漏的检查完所有执行上下文和全局的引用位置,虚拟机应当是有办法直接得到哪些地方存放着对象的引用。在HotSpot的解决方案里,是使用一组称为OopMap的数据结构来达到这个目的

        类加载完成时,HotSpot就会把对象内什么偏移量上是什么类型的数据计算出来,在即时编译过程中,也会在特定的位置记录下栈里和寄存器里哪些位置时引用。这样收集器扫描时就可以直接知道这些信息,并不需要真正一个不漏从方法区等GC Roots考试查找。

        下面的代码是 HotSpot 虚拟机客户端模式下生成的一段 String::hashCode() 方法的本地代码,可以看到在0x026eb7a9 处的 call 指令有 OopMap 记录,它指明了 EBX 寄存器和栈中偏移量为 16 的内存区域 中各有一个普通对象指针(Ordinary Object Pointer OOP )的引用,有效范围为从 call 指令开始直到0x026eb730(指令流的起始位置) +142 OopMap 记录的偏移量) =0x026eb7be ,即 hlt 指令为止。

二、安全点

        HotSpot没有为每一条指令都生成OopMap,上面提到的“类加载完成时,HotSpot就会把对象内什么偏移量上是什么类型的数据计算出来,在即时编译过程中,也会在特定的位置记录下栈里和寄存器里哪些位置时引用中提到的特定的位置记录了这些信息,这些位置被称为安全点(Safepoint)。因此,用户程序执行时并非在任意位置都能停下来进行垃圾收集,强制要求必须执行到安全点后才能暂停。所以,安全点的选定既不能太少以至于让收集器等待时间过长,也不能太频繁以至于过分增大运行时的内存负荷

        安全点位置的选取标准:是否具有让程序长时间执行的特征。因为每条指令执行的时间都非常短暂,程序不太可能因为指令流长度太长这样的原因而长时间执行,长时间执行的最明显特征就是指令序列的复用,例如方法调用、循环跳转、异常跳转等都属于指令序列复用,所以只有具有这些功能的指令才会产生安全点。

如何让所有线程都跑到最近的安全点停顿下来:

抢先式中断

        抢先式中断不需要线程的执行代码主动去配合。
        在垃圾收集发生时,系统首先把所有用户线程全部中断,如果发现有用户线程中断的地方不在安全点上,就恢复这条线程执行,让它一会儿再重新中断,直到跑到安全点上。
        现在几乎没有虚拟机实现采用抢先式中断来暂停线程响应GC事件。

主动式中断

        设置一个标志位,线程执行时会不停的主动轮询这个标志,一旦发现中断标志位为真自己在最近的安全点上主动中断挂起

        轮询标志的地方和安全点是重合的,另外还要加上所有创建对象和其他需要在Java堆上分配内存的地方,这是为了检查是否即将要发生垃圾收集,避免没有足够内存分配新对象。
        由于轮询操作在代码中会频繁出现,这要求它必须足够高效。HotSpot 使用内存保护陷阱的方式,把轮询操作精简至只有一条汇编指令的程度。
        下面代码清单中的 test 指令就是 HotSpot 生成的轮询指令,当需要暂停用户线程时,虚拟机把0x160100 的内存页设置为不可读,那线程执行到 test 指令时就会产生一个自陷异常信号,然后在预先注册的异常处理器中挂起线程实现等待,这样仅通过一条汇编指令便完成安全点轮询和触发线程中断了。

 

三、安全区域

        程序“不执行”时(没有分配处理器时间,如sleep和blocked状态),线程无法响应虚拟机的中断请求,不能走到安全点主动挂起,虚拟机也不可能等线程重新被激活。所以引入安全区域。

        安全区域是指能够确保在某一段代码片段之中,引用关系不会发生改变。因此,在这个区域中任意地方开始垃圾收集都是安全的。可以看作扩展延伸了的安全点。

        进入安全区域的代码,会标识自己已经进入安全区域,虚拟机发起垃圾收集时不用去管这些线程。当线程要离开安全区域时,他要检查虚拟机是否已经完成了根节点枚举(或垃圾收集过程中其他需要暂停用户线程的阶段),完成,那线程就当作没事发生,继续执行。否则一直等待直到收到可以离开安全区域的信号。

四、记忆集与卡表

        在分代收集中,为了解决对象跨代引用所带来的问题,在新生代中建立记忆集的数据结构,用以避免把整个老年代加进GC Roots扫描范围。事实上所有部分区域收集行为的垃圾收集器都会有跨代引用问题。

        记忆集是一种记录从非收集区域指向收集区域指针集合的抽象数据结构。不考虑效率和成本,最简单的实现用非收集区域中所有含跨代引用的对象数组来实现这个数据结构。

        在垃圾收集场景中,收集器只需要通过记忆集判断出某一块非收集区域是否存在指向收集区域的指针就行,不需要了解跨代指针的全部细节

        设计者在实现记忆集的时候,便可以选择更为粗犷的记录粒度来节省记忆集的存储和维护成本,下面列举了一些可供选择(当然也可以选择这个范围以外的)的记录精度:

  • 字节精度:每个记录精确到机器字长(就是处理器的寻址位数,如常见的32位或64位,这个精度决定了机器访问物理内存地址的指针长度),该字包含跨代指针。
  • 对象精度:每个记录精确到一个对象,该对象里有字段含有跨代指针。
  • 卡精度:每个记录精确到一块内存区域,该区域内有对象含有跨代指针。(用一种称为“卡表”的方式去实现记忆集,是常用记忆集实现方式之一。
        记忆集其实是一种“抽象 的数据结构,抽象的意思是只定义了记忆集的行为意图,并没有定义其行为的具体实现。卡表就是记忆集的一种具体实现,它定义了记忆集的记录精度、与堆内存的映射关系等。

卡表最简单的形式可以只是一个字节数组,下面这行代码是HotSpot默认的卡表标记逻辑:

CARD_TABLE[this address >> 9] = 0;
        字节数组CARD_TABLE 的每一个元素都对应着其标识的内存区域中一块特定大小的内存块,这个内存块被称作“ 卡页 Card Page )。一般来说,卡页大小都是以 2 N 次幂的字节数,通过上面代码可以看出HotSpot 中使用的卡页是 2 9 次幂,即 512 字节(地址右移 9 位,相当于用地址除以 512 )。那如果卡表标识内存区域的起始地址是0x0000 的话,数组 CARD_TABLE 的第 0 1 2 号元素,分别对应了地址范围为0x0000 0x01FF 0x0200 0x03FF 0x0400 0x05FF 的卡页内存块,如下图所示。
        一个卡页的内存中通常包含不止一个对象,只要卡页内有一个(或更多)对象的字段存在着跨代指针,那就将对应卡表的数组元素的值标识为1,称为这个元素变脏( Dirty ),没有则标识为 0 。在垃圾收集发生时,只要筛选出卡表中变脏的元素,就能轻易得出哪些卡页内存块中包含跨代指针,把它们加入GC Roots 中一并扫描。

五、写屏障

        何时变脏有其他分代区域中对象引用了本区域对象时,其对应的卡表元素就应该变脏,变脏时间点原则上应该发生在引用类型字段赋值的那一刻。

        如何变脏,即如何更新维护卡表

  • 若是解释执行的字节码,虚拟机负责每条字节码指令的执行,有充分的介入空间。
  • 若是编译执行,经过即时编译后的代码已经是纯粹的机器指令流了,这就必须找到一个在机器码层面的手段,把维护卡表的动作放到每一个赋值操作之中。

        HotSpot通过写屏障(Writer Barrier)技术维护卡表。写屏障可以看作在虚拟机层面对“引用类型字段赋值”这个动作的AOP切面,在引用对象赋值时会产生一个环形(Around)通知,供程序执行额外的动作,也就是说赋值前后都在写屏障的覆盖范围内。在赋值前的部分的写屏障叫做写前屏障(Pre-Write Barrier),赋值后则叫写后屏障(Post-Write Barrier)。

G1之前只用到写后屏障。

写后屏障更新卡表,如下图。

        除了写屏障开销外(相较于扫描整个老年代的代价低),卡表在高并发下面临着“伪共享”问题。中央处理器的缓存系统是以缓存行为单位存储,多线程修改独立变量,这些变量恰好共享同一个缓存行,就会彼此影响(写回,无效化或同步)而导致性能降低。

        解决伪共享办法:不采用无条件的写屏障,先检查卡表标记,只有卡表元素未被标记过时才将其标记变脏,即卡表更新逻辑变为:

if(CARD_TABLE[this address >> 9] != 0)CARD_TABLE[this address >> 9] = 0;
        在JDK 7 之后, HotSpot 虚拟机增加了一个新的参数 -XX +UseCondCardMark ,用来决定是否开启卡表更新的条件判断。开启会增加一次额外判断的开销,但能够避免伪共享问题,两者各有性能损耗,是否打开要根据应用实际运行情况来进行测试权衡。

六、并发的可达性分析

        在根节点枚举这个步骤中,GC ROOTS相比起整个java堆中全部的对象已经减少了很多,且在各种优化技巧(如OopMap)的加持下,它带来的停顿已经非常短暂且相对固定。可从GC Roots继续往下遍历对象图,这一步骤的停顿时间必定与java堆容量成正比关系:堆越大,存储的对象越多,对象图结构越复杂,要标记更多对象而产生的停顿时间的更长

        要知道包含“ 标记 阶段是所有追踪式垃圾收集算法的共同特征,如果这个阶段会随着堆变大而等比例增加停顿时间,其影响就会波及几乎所有的垃圾收集器,同理可知,如果能够削减这部分停顿时间的话,那收益也将会是系统性的。
首先了解一下为什么在一个能保障一致性的快照下才能进行对象图的遍历?我们使用三色标记辅助推导:
  • 白色:对象尚未被垃圾收集器访问到。在可达性分析刚刚开始阶段,所有阶段对象都是白色,分析结束阶段,仍为白色,即代表不可达。
  • 黑色:对象已经被垃圾收集器访问过,且这个对象的所有引用都已经扫描过。黑色对象代表已经扫描过,它是安全存活的,如有其他对象引用指向黑色对象,无须重新扫描。黑色对象不可能直接(不经过灰色对象)指向某个白色对象。
  • 灰色:对象已经被垃圾收集器访问过,但这个对象上至少存在一个引用还没有被扫描过。

        可达性分析的扫描过程,可以看作对象图上一股以灰色为波峰的波纹从黑向白推进的过程。用户线程冻结不会有任何问题。但用户线程并发,收集器在标记时,用户线程在修改引用,会导致两种结果:一种是把原本消亡的对象错误标记为存活,即产生浮动垃圾,下次收集即可,可以容忍。另一种是把原本存活的对象标记为已消亡,这就很致命了,程序肯定会因此发生错误,下面演示这样的致命错误是怎样产生的。

“对象消失”问题:原本应该是黑色的对象被误标为白色。

“对象消失”问题产生的条件(需要同时满足):

  • 赋值器插入了一条或多条从黑色对象到白色对象的新引用
  • 赋值器删除了全部从灰色对象到该白色对象的直接或间接引用

解决“对象消失”问题:增量更新原始快照

  • 增量更新:破坏第一个条件,当黑色对象插入新的指向白色对象的引用关系时,就将这个新插入的引用记录下来,等并发扫描结束后,再将这些记录过的引用关系中的黑色对象为根,重新扫描一次。简化理解为:黑色对象一旦新插入了指向白色对象的引用之后,他就变回灰色对象了。
  • 原始快照:破坏第二个条件,当灰色对象要删除指向白色对象的引用关系时,就将这个要删除的引用记录下来,在并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根,重新扫描一次。简化理解为:无论引用关系删除与否,都会按照刚刚开始扫描那一刻的对象图快照来进行搜索。
以上无论是对引用关系记录的插入还是删除,虚拟机的记录操作都是通过写屏障实现的。
        在 HotSpot虚拟机中,增量更新和原始快照这两种解决方案都有实际应用,譬如,CMS 是基于增量更新来做并发标记的,G1 Shenandoah 则是用原始快照来实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/93216.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

辽宁线上3D三维虚拟工厂生产仿真系统应用场景及优势

工厂虚拟仿真是一种基于计算机技术和虚拟现实技术的数字化解决方案,它可以通过模拟工厂中的设备、流程和操作,来为工程师和操作人员提供了一个沉浸式的虚拟环境,帮助他们更好地了解和优化工厂生产过程。 工厂VR三维可视化技术为工业生产提供了…

W5100S-EVB-PICO 做UDP Server进行数据回环测试(七)

前言 前面我们用W5100S-EVB-PICO 开发板在TCP Client和TCP Server模式下,分别进行数据回环测试,本章我们将用开发板在UDP Server模式下进行数据回环测试。 UDP是什么?什么是UDP Server?能干什么? UDP (User Dataqram …

什么是可视化编程?为什么它如此重要?

可视化编程,又叫可视化程序设计,一直以来就是备受讨论的“热门技术”。一方面,程序员抵触它,觉得它不如用代码开发。另一方面,对于产品经理等稍微懂点开发的业余人员,它确实能提供价值。所以,它…

tomcat服务七层搭建动态页面查看

一个服务器多实例复制完成 配置tomcat多实例的环境变量 vim /etc/profile.d/tomcat.sh配置tomcat1和tomcat2的环境变量 进入tomcat1修改配置 测试通信端口是否正常 连接正常 toncat 2 配置修改 修改这三个 端口配置修改完成 修改tomcat1 shudown 分别把启动文件指向tomcat1…

AgentBench::AI Agent 是大模型的未来

最有想象力、最有前景的方向 “Agent 是 LLM(大语言模型)的最有前景的方向。一旦技术成熟,短则几个月,长则更久,它可能就会创造出超级个体。这解释了我们为何对开源模型和 Agent 兴奋,即便投产性不高,但是我们能想象自己有了 Agent 之后就可以没日没夜地以百倍效率做现在…

mysql 8.0.20不停机主从同步

一、环境 CentOS : 7.3.1611 (Core) mysql:8.0.20 二、遇到的问题 1.查看主从同步发现下列问题 error connecting to master repl192.168.0.21:3306 - retry-time: 60 retries: 4 message: Authentication plugin caching_sha2_password reported e…

java版鸿鹄工程项目管理系统 Spring Cloud+Spring Boot+前后端分离构建工程项目管理系统源代码

鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展,企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性,公司对内部工程管…

SpringBoot整合Minio

SpringBoot整合Minio 在企业开发中&#xff0c;我们经常会使用到文件存储的业务&#xff0c;Minio就是一个不错的文件存储工具&#xff0c;下面我们来看看如何在SpringBoot中整合Minio POM pom文件指定SpringBoot项目所依赖的软件工具包 <?xml version"1.0" …

【Android Studio】 win11 安装配置 jdk17 超详细

概述 一个好的安装教程能够帮助开发者完成更便捷、更快速的开发。书山有路勤为径&#xff0c;学海无涯苦作舟。我是秋知叶i、期望每一个阅读了我的文章的开发者都能够有所成长。 一、下载JDK JDK官网 这里下载 JDK17 windows x64 installer 二、安装JDK 双击打开下载的 j…

上山取石&#xff0c;下江取锦。诗人秋浦啸傲&#xff0c;新津樵唱。江南山水秀美&#xff0c;水乡文化流长。而水&#xff0c;则是这些山水风景的灵魂所在。 水&#xff0c;雨露滋润万物生长的泉源。 它潺潺流淌于山间溪涧&#xff0c;涓涓细流化成了青山的眼泪。水顺势而下&a…

互联网发展历程:跨越远方,路由器的启示

互联网的蓬勃发展&#xff0c;一直在追求更广阔的连接&#xff0c;更遥远的距离。然而&#xff0c;在早期的网络中&#xff0c;人们面临着连接距离有限的问题。一项重要的技术应运而生&#xff0c;那就是“路由器”。 连接受限的问题&#xff1a;距离有限 早期的网络受限于直接…

认识excel篇3之数据的有效性(数据验证)

数据有效性不仅能够对单元格的输入数据进行条件限制&#xff0c;还可以在单元格中创建下拉列表菜单方便用户选择输入。如果没有做数据验证&#xff0c;单元格内默认可以输入任意类型的数据。数据验证就是限制单元格输入数据&#xff08;必须输入符合要求的才能输入&#xff09;…

基于Prometheus监控Kubernetes集群

目录 一、环境准备 1.1、主机初始化配置 1.2、部署docker环境 二、部署kubernetes集群 2.1、组件介绍 2.2、配置阿里云yum源 2.3、安装kubelet kubeadm kubectl 2.4、配置init-config.yaml 2.5、安装master节点 2.6、安装node节点 2.7、安装flannel、cni 2.8、部署测…

[机器学习]特征工程:特征降维

特征降维 1、简介 特征降维是指通过减少特征空间中的维度&#xff0c;将高维数据映射到一个低维子空间的过程。 在机器学习和数据分析中&#xff0c;特征降维可以帮助减少数据的复杂性、降低计算成本、提高模型性能和可解释性&#xff0c;以及解决维度灾难等问题。特征降维通…

中远麒麟堡垒机 SQL注入漏洞复现

0x01 产品简介 中远麒麟依托自身强大的研发能力,丰富的行业经验&#xff0c;自主研发了新一代软硬件一体化统一安全运维平台一-iAudit 统一安全运维平台。该产品支持对企业运维人员在运维过程中进行统一身份认证、统一授权、统一审计、统一监控&#xff0c;消除了传统运维过程中…

ORCA优化器浅析——CDXLScalar Base class for representing scalar DXL operators

CDXLScalar类作为Base class for representing scalar DXL operators&#xff0c;该类只是定义一些接口&#xff0c;其中实现了GetDXLOperatorType函数&#xff0c;其返回EdxloptypeScalar&#xff0c;代表scalar DXL operators。 class CDXLScalar : public CDXLOperator{ pr…

nodejs+vue+elementui膳食营养健康网站 4d8g9

前端nodejsvueelementui, 后台主要功能是&#xff1a;首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;膳食类型管理&#xff0c;膳食信息管理&#xff0c;膳食食材管理&#xff0c;食材类型管理&#xff0c;我的收藏管理&#xff0c;网站管理&#xff0c;订单管理等…

2023机械制造ERP管理软件哪个好?亿发现代化ERP系统建设,免费更新

机械制造行业市场大&#xff0c;不但需要满足传统行业的需求&#xff0c;还有新能源汽车、智能家居、装备制造等新兴领域的需求。而随着人工智能、物联网、大数据等科技的应用&#xff0c;机械制造业正逐步向智能化、自动化方向发展&#xff0c;增强生产效率和产品质量。在机械…

PHP使用phpmailer及SMTP服务实现邮件发送

博客升级中&#xff0c;把之前没有想到的功能一点点的完善。 这篇日志记录一下&#xff0c;使用phpmailer实现邮件发送的这样一个操作。 博客偶尔会有留言和评论&#xff0c;我也会及时回复&#xff0c;但是有一个问题&#xff0c;我回复了&#xff0c;给我留言的人如果不再次…

大模型是什么?大模型可以在哪些场景应用落地?

大模型是什么&#xff1f;大模型是指模型具有庞大的参数规模和复杂程度的机器学习模型。在深度学习领域&#xff0c;大模型通常是指具有数百万到数十亿参数的神经网络模型。 大模型是指模型具有庞大的参数规模和复杂程度的机器学习模型。在深度学习领域&#xff0c;大模型通常是…