JVM—垃圾收集算法和HotSpot算法实现细节

news2025/1/17 0:59:45

1、分代回收策略

  • 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。

  • 分代垃圾回收采用分治的思想,进行代的划分,把不同生命周期放在不同代上,不同代采用最适合它的垃圾回收方法进行回收。

1.1 代际划分

1.2 垃圾回收

1.2.1 年轻代(新生代)

年轻代会划分出Eden区域与两个大小对等的Survivor区域。其比例一般为8:1:1,这是因为根据统计95%的对象朝生夕死,存活时间极短。

  • 当新对象生成,并且在Eden申请空间失败时,就会触发GC,清理Eden中的非存活对象,并且把存活的对象移动到Survivor区,接下来整理两个Survivor区域。

  • 这种方式不会影响到年老代,因为大部分对象都是从Eden区开始的,所以Eden区垃圾回收十分频繁,需要快速、高效的算法(一般采用复制算法)。

1.2.2 年轻代收集(Minor GC/Young GC)

  1. 当新对象在Eden区域内存分配失败时就会触发年轻代的垃圾回收,称之为“Minor GC”

  2. 每个对象都有一个年龄,这个年龄就是指对象经历过Minor GC的次数。

如图一所示,对象刚分配到Eden时年龄为“0”,当Minor GC被触发时,所有存活的对象会被拷贝到其中一个Survivor区域中,同时年龄增长“1”,最后清除Eden区域非可达对象

如图二所示,当第二次Minor GC被触发时、JVM会通过Mark算法 (标记算法)找出Eden区域和Survivor1区域存活的对象并将他们拷贝到新的Survivor2区域,同时年龄增长加“1”,最后清除Eden区域和Survivor1区域非可达对象。

如图三所示,当对象年龄足够大(年龄通过JVM参数设置),并且Minor GC再次发生,他就会从Survivor内存区域升级到年老代中。

1.2.3 年老代

  • 年老代是用来存放长时间存活的对象的内存区域。

  • 当对象在新生代中经历了多次垃圾回收仍然存活时,它们会被晋升到年老代。

  • 年老代的垃圾回收频率较低,并且每次回收的成本较高

在年老代选择的垃圾回收算法取决于JVM采用的什么垃圾回收器

1.2.4 年老代收集(Major GC/Old GC)

  1. Minor GC发生时,又有对象从Survivor区域升级到Tenured区域,但是Tenured区域没有空间容纳新的对象,此时就会触发年老代的垃圾回收。

  2. 年老代的垃圾回收算法取决于JVM选择的垃圾回收器。

2、 垃圾收集算法(Mark)

2.1 标记-清除算法(Mark-Sweep)

过程:

算法分为标记、清除两个过程。

  1. 首先标记出所有需要回收的对象/标记存活的对象。

  2. 接下来清除未被标记的对象。

标记清除算法是最基础的算法,后续收集算法基本上都是以标记-清除算法为基础。

标记-清除算法的主要缺点有两个:

  1. 执行效率不稳定,如果 Java堆中有大量对象并且其中大部分需要回收,这时必须进行大量的标记清除动作导致两个过程执行效率随对象数量增加而增大。

  2. 空间碎片化严重,会产生大量不连续的空间碎片,碎片太多分配较大对象会导致没有足够的连续内存从而发生垃圾回收。

标记-清除算法执行过程如图所示:

2.2 标记-复制算法(Mark-Copy)

为了解决标记-清除算法面对大量可回收对象时执行效率低的问题,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。

过程:

  1. 当其中一块内存用完时,将存活的对象复制到另一块上面。

  2. 然后清除使用过的这块内存。

标记-复制算法的缺点:

  1. 如果内存大多对象都是存活的,这种算法会产生大量的内存复制间的开销。

  2. 复制算法的代价就是将可用内存缩小了原来的一半,空间浪费较大。

标记-复制算法的执行过程:

2.3 标记-整理算法(Mark-Compact)

针对于年老代对象的存亡特征,提出了标记-整理算法,其标记过程和“标记-清除”算法一样,但是后续不是清除可回收对象而是让所有存活的对象都向内存空间一端移动

  • 标记-清除算法和标记-整理算法本质差异在于前者是一种非移动式的回收算法,而后者是移动的。

  • 优点:这种方式避免碎片内存的产生,又不需要两块相同的内存

  • 缺点:压缩操作需要进行局部对象移动

3、HotSpot算法的实现细节

3.1 根节点枚举

所谓“一致性”就是整个枚举期间不会出现枚举过程中,整个根节点集合的对象引用在不断发生变化。

  • 主流Java虚拟机都是使用的准确式垃圾收集。当用户线程停下时,不需要一个不漏的检查所有执行上下文和全局引用位置。

在HotSpot的解决方案中:

  1. 使用一组称为OopMap的数据结构来达到目的。

  2. 一旦类加载完成,HotSpot就会将对象内什么偏移量上是什么类型的数据计算出来。

  3. 在即时编译中也会在特定位置安全点)记录栈和寄存器里哪些位置是引用。

这样收集器扫描就可以直接得到这些信息,而不需要一个不漏的从方法区等待GC Roots查找。

3.2 安全点

  • 导致OopMap内容变化的指令非常多,如果为每个指令生成对应的OopMap将需要大量的额外存储空间。

  • 安全点决定了用户执行时不能在代码指令流的任意位置停下来进行GC,必须强制到达安全点才能执行GC。

  • 安全点一般选取在“是否具有长时间执行特征”的地方,例如方法调用、循环跳转、异常跳转。

如何让垃圾收集发生时所有线程都跑到最近的安全点,然后停顿?

  1. 抢先式中断:在GC发生时,系统把用户线程全部中断,如果发现有中断的线程不在安全点上,就恢复这条线程执行,让它直到跑到安全点上中断。(现在几乎没有虚拟机采用这种方式)

  2. 主动式中断:需要中断线程时,不直接操作线程而是简单的设置一个标志位,各个线程执行时会不停的主动询问这个标志。一旦这个标志位为真就在自己最近的安全点上主动中断挂起。

3.3 安全区域

安全点似乎已经完美解决了停顿用户线程,让虚拟机进行垃圾回收状态。但是当程序“不执行”时,安全点就没有作用。

典型场景:用户线程处于Sleep状态/Blocked状态,这时线程无法响应虚拟机中断请求,不能走到安全的地方去中断挂起自己。

  • 安全区是指确保在某一代码片段中,引用关系不会发生变化。因此这个区域任意地方发生垃圾回收都是安全的。

  • 当用户线程执行安全区的代码,首先会标记自己已经进入了安全区域,这段时间虚拟机发起GC就不会管已声明自己在安全区的线程。

  • 当线程需要离开安全区时,它会先检查虚拟机是否完成根节点枚举(或者GC过程中其他需要暂停用户线程的阶段)。如果完成那么线程就会继续执行,否则就会一直等待,直到收到离开安全区域的信号为止。

3.4 记忆集与卡表

在分代收集理论中,为了解决对象跨代引用所带来的问题,垃圾收集器在新生代中建立了名为记忆集的数据结构。

记忆集是一种用于记录从非收集区指向收集区域指针集合的抽象数据结构,它用于缩减GC Roots扫描范围。

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

设计实现记忆集时,可以选择不同的记录粒度来节省记忆集的存储和维护成本:

第三种卡精度是用一种“卡表”的方式去实现记忆集,也是目前最常用实现记忆集的形式。 HotSpot虚拟机卡表的实现:

  1. 卡表最简单的形式可以是一个字节数组。

  1. 字节数组CARD_TABLE的每个元素都对应着其标识的内存区域中一块特定大小的内存块。

  2. 一个卡页中通常存在不止一个对象,只要卡页有一个对象存在跨代指针,就将对应的卡表数组元素标记为1,称“元素变脏”。

  3. 在垃圾收集时只需要,筛选卡表中变脏的元素,就能得到哪些卡页内存包含跨域指针,把它们加入GC Roots中扫描。

3.5 写屏障

我们使用记忆集缩减GC Roots扫描的范围问题,但是还没解决卡表元素如何维护,什么时候变脏,谁来让它变脏?

  • 什么时候变脏?——有其他分代区域的对象引用了本区域对象时,对应卡表元素就应该变脏。

HotSpot通过写屏障来维护卡表状态,帮助虚拟机跟踪对象引用的变化。

  • 应用写屏障后,虚拟机就会为赋值操作生成相应指令,一旦收集器在写屏障增加了更新卡表的操作,无论更新是不是年老代对新生代对象的引用,每次对引用更新就会产生额外开销。但是这个开销远小于扫描这个年老代的开销。

  • 卡表在高并发下还存在“伪共享”的问题。

  1. 现代CPU缓存系统是以缓存行为单位存储的

  2. 多线程下修改互相独立的变量时,这些变量如果共享同一个缓存行,就会彼此影响导致性能降低。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

π 随机数计算圆周率

如下图&#xff0c;圆与正方形面积比值等于圆内的点和总点数的比值 Disatance是点到原点的距离&#xff1a;SQRT(A2xA2B2xB2) 以下是在Excel中1.5万个点计算圆周率的结果。 InCircle公式&#xff1a;IF(C2<1,1,0) π计算公式&#xff1a;4*SUM(D2:D15000)/COUNT(D2:D15000)

leetcode 2181.合并零之间的结点

1.题目要求: /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* mergeNodes(struct ListNode* head){struct ListNode* cur head;int count 0;//1.遍历结点&#xff0c;求出结点数while(cur){co…

前后端demo-WarehouseManagement

前端 数据库 其他 1.git下来&#xff0c;解决依赖问题&#xff0c;前端报错因为字体文件丢失&#xff0c;下载字体放到fonts文件夹字体.zip官方版下载丨最新版下载丨绿色版下载丨APP下载-123云盘 2.后端login验证&#xff0c;前端需要账号格式&#xff0c;linqq.com 3.自己…

【React】实现消息列表的删除

工作小记&#xff0c;第一次接触react项目 1.增加删除对话项的函数 hooks\use-conversation.ts // 删除对话项的函数const deleteConversation (id: string) > {setConversationList(prevList > prevList.filter(item > item.id ! id))}return {deleteConversation,.…

OZON户外运动装备热卖产品

OZON平台上的户外运动装备热卖产品涵盖了多个类别&#xff0c;这些产品不仅满足了俄罗斯消费者对户外运动的需求&#xff0c;还体现了市场趋势和消费者偏好的变化。以下是一些主要的热卖户外运动装备产品&#xff1a; OZON户外运动装备热卖产品地址&#xff1a;D。DDqbt。COm/…

第十九天培训笔记

上午 1 、构建 vue 发行版本 [rootserver eleme_web]# nohup npm run serve& // 运行 vue 项目 [rootserver eleme_web]# mkdir /eleme [rootserver eleme_web]# cp -r /root/eleme_web/dist/* /eleme/ // 将项目整体 移动到 /eleme 目录下 [rootserver eleme_web]# …

区间贪心2

问题引入 上一节我们学习了贪心的基本概念、基本思路以及证明方法&#xff0c;下面我们一起来学习区间类贪心类问题&#xff0c;这里我们学习的重点依然是贪心的策略和证明。 算法原理区间贪心&#xff1a; 是指在区间上做贪心。区间贪心一般都是按左端点或者右端点排序&…

最短路问题中的朴素版Dijkstra算法

最短路问题的朴素版Dijkstra算法 题目 最短路问题需要用到下面的算法&#xff08;n代表点的数量&#xff0c;m代表边的数量&#xff09; 朴素版和堆优化版的Dijkstra算法的区别是&#xff0c;朴素版比较适合稠密图&#xff0c;堆优化版适合稀疏图&#xff0c;稠密图代表它的边…

模型优化—动量梯度下降

一、mini-batch 梯度下降&#xff08;gradient descent&#xff09;&#xff1a; SGD&#xff08;stochastic GD&#xff09;随机梯度下降&#xff1a;对一个样本做梯度下降 batch梯度下降&#xff1a;使用所有样本做梯度下降&#xff08;做一次又叫epoch&#xff09; mini…

人工智能算法工程师(高级)课程9-自然语言处理之词嵌入的介绍与代码详解

大家好,我是微学AI,今天给大家介绍一下人工智能算法工程师(高级)课程9-自然语言处理之词嵌入的介绍与代码详解。 词嵌入是一种将文本中的词语转换为数值向量的技术,广泛应用于自然语言处理领域。它通过将词语映射到多维向量空间,使得相似意义的词语在向量空间中距离较近,从…

【无标题配置jdk环境和tomcat环境

一&#xff0e;接着昨天的发布vue项目 npm run serve 构建项目 npm run build ls ls dist/ vim dist/index.html [rootweb eleme_web]# cd /usr/local/nginx/conf/ [rootweb conf]# ls 将静态的项目移动到nginx中 [rootweb nginx]# cd conf.d/ [rootweb conf.d]# ls…

用Python打造精彩动画与视频,3.3 添加音频和简单效果

3.3 添加音频和简单效果 在本节中&#xff0c;我们将学习如何使用 MoviePy 库为视频添加音频和一些简单的效果。这些操作可以让你的视频更具吸引力和个性化。 准备工作 首先&#xff0c;确保你已经安装了 MoviePy 和 pydub 库。你可以通过以下命令安装&#xff1a; pip ins…

Qt 实战(2)搭建开发环境 | 2.4、查看 Qt 源码

文章目录 一、查看 Qt 源码1、获取 Qt 源码2、添加源码路径3、配置定位器4、查看源码 前言&#xff1a; Qt 是一个跨平台的 C 图形用户界面应用程序开发框架&#xff0c;广泛应用于开发 GUI 程序以及非 GUI 程序&#xff0c;如控制台工具和服务器。查看 Qt 的源码不仅可以帮助你…

故障案例:网络访问慢

现象描述 FW作为中间设备的场景下&#xff0c;用户访问网页慢&#xff0c;报文延时大等。 相关告警与日志 相关告警 无 相关日志 ARP/4/ARP_DUPLICATE_IPADDR 原因分析 图1 网络访问慢故障定位思路 丢包 报文在网络链路上传输时&#xff0c;可能会有部分报文在链路中被丢…

用深度学习改进乳腺癌MRI诊断| 文献速递--AI辅助的放射影像疾病诊断

Title 题目 Improving breast cancer diagnostics with deep learning for MRI 用深度学习改进乳腺癌MRI诊断 01 文献速递介绍 乳腺磁共振成像&#xff08;MRI&#xff09;是一种检测乳腺癌的高度敏感的方式&#xff0c;报告的敏感性超过80%。传统上&#xff0c;其在筛查…

【算法】动态规划-斐波那契数列模型

目录 1、第N个泰波那契数 1.1 算法原理讲解 1.1.1 状态表示 1.1.2 状态转移方程 1.1.3 初始化 1.1.4 填表顺序 1.1.5 返回值 1.2 代码实现 1.3 空间优化 2、三步问题 2.1 算法原理讲解 2.1.1 状态表示 2.1.2 状态转移方程 2.1.3 初始化 2.1.4 填表顺序 2.1.5 返…

(四十一)大数据实战——spark的yarn模式生产环境部署

前言 Spark 是一个开源的分布式计算系统。它提供了高效的数据处理能力&#xff0c;支持复杂的数据分析和处理任务&#xff0c;是一种基于内存的快速、通用、可扩展的大数据分析计算引擎。Spark Core&#xff1a;实现了Spark的基本功能&#xff0c;包含任务调度、内存管理、错误…

上线前端系统

上线一个静态的前端系统&#xff08;续&#xff09; 在eleme服务器上 启动服务 启动rpcbind [rooteleme-static ~]# systemctl restart rpcbind 启动nfs [rooteleme-static ~]# systemctl restart nfs 重启服务 启动smb [rootstatic-server img]# systemctl start smb…

SQL数据库模糊查询指定的字符的表资料(CHARINDEX)

1.目的 MSG栏位里面有很多组合内容的字符信息&#xff0c;需要进行模糊查询。 2.问题 正常使用LIKE 语句可以通用大部分的查询需求&#xff0c;但是遇到部分的特殊字符&#xff0c;例如&#xff1a;[] 资料是存在数据资料中&#xff0c;但是查询反馈的结果是没有内容&#xf…

二刷代码随想录训练营Day 16|513.找树左下角的值、112.路径总和、106.从中序与后序遍历序列构造二叉树

1.找到左下角的值 513. 找树左下角的值 - 力扣&#xff08;LeetCode&#xff09;代码随想录 (programmercarl.com) 代码&#xff1a; class Solution { public:int maxDepth INT_MIN;int result;// 深度最大&#xff0c;确保是最后一行 先遍历左孩子再遍历右孩子 确保是左下…