详解垃圾回收算法,优缺点是什么?|金三银四系列
本文详细介绍了在 JVM 中如何判断哪些对象是需要回收的,以及不同的垃圾回收算法以及优缺点。
点击上方“后端开发技术”,选择“设为星标” ,优质资源及时送达
上篇文章详细介绍了 JVM 的结构以及其内存结构,需要阅读请移步。本文主要讲解 JVM 体系中的垃圾收集子系统用到的内存回收算法。
八股文总是忘?一张图牢记JVM内存结构|金三银四系列
2023-02-13

哪些对象需要回收
在进行垃圾回收之前,第一件事就是确定哪些对象还存活着,哪些对象已死需要被回收。
1.引用计数算法
很多判断对象是否存活的算法是这样的:
给对象中添加一个引用计数器,每当有个地方引用它时,计数器值就加1;
当引用失效时,计数器值就减1,任何时刻计数器为0的对象就是不可能再被使用的,该对象将被回收。
客观的说,引用计数法实现简单,效率高。但是没有主流JVM选择引用计数法管理内存,因为他无法解决循环引用的问题。如下图
当三个对象都不在使用时,因为相互的引用关系,并不会置为0,所以难以被回收。并且,引用计数器要求在每次因引用产生和消除的时候,伴随一个加法操作和减法操作,对系统性能会有一定的影响。
2.可达性分析算法
主流JVM都是称通过可达性分析来判定对象是否存活的。这个算法的基本思路就是通过一系列的称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链( Reference Chain),当一个对象到GC Roots 没有任何引用链相连,用图论的话来说,就是从GC Roots 到这个对象不可达) 时,则证明此对象是不可用的。
如图所示,深灰色的对象虽然互相有关联,但是它们到 GC Roots 是不可达的,所以它们将会被判定为是可回收的对象。

GC Root 对象
Java中,有一组GC Roots对象,它们是被垃圾回收器直接管理的对象。GC Roots对象是垃圾回收的起始点,垃圾回收器会从这些对象开始遍历所有的对象,并标记出所有存活的对象,未被标记的对象即为可回收对象。
在Java中,可以作为GC Root对象的包括以下几类:
虚拟机栈(栈帧中的本地变量表)中引用的对象。虚拟机栈中保存着每个线程执行方法时的现场信息,包括局部变量、操作数栈、方法出口等。栈帧中的本地变量表中存放着方法中定义的局部变量和参数,如果局部变量或参数中引用了某个对象,则该对象被认为是一个GC Root对象。
方法区中类静态属性引用的对象。类似于栈帧中的本地变量表,类中定义的静态属性也会保存引用对象。因为静态属性是属于类的,所以这些引用对象也被认为是GC Root对象。
方法区中常量引用的对象。常量池中保存着编译器生成的各种字面量和符号引用,例如字符串常量、类和接口的全限定名、字段和方法的名称和描述符等。如果常量池中引用了某个对象,则该对象被认为是GC Root对象。
本地方法栈中JNI(即Java Native Interface)引用的对象。JNI是Java提供的一种机制,用于调用本地的C/C++代码。如果JNI中引用了某个对象,则该对象被认为是GC Root对象。
需要注意的是,不是所有的对象都可以作为GC Root对象。例如,普通的对象实例和数组元素就不可以作为GC Root对象。只有与Java虚拟机内存管理有关的对象才可以作为GC Root对象,因为它们直接或间接地引用了所有其他对象。
垃圾回收算法
1.标记-清除算法
最基础的收集算法是标记-清除算法(Mark-Sweep),如同它的名字一样,算法分为“标记”和“清除”两个阶段:
首先标记出所有需要回收的对象(可达性分析)。
在标记完成后统一回收所有被标记的对象。
之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其不足进行改进而得到的。

优点
实现简单,算法简单、容易实现,与保守式GC 算法兼容,有效处理不连续的内存空间
不需要移动对象,因此不会浪费时间和空间。
不足
效率问题:标记和清除两个过程的效率都不高,且需要进行两次扫描,第一次标记垃圾对象,第二次清除垃圾对象。
空间问题:标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作,从而导致频繁的内存分配和回收。
2.标记-压缩算法(标记-整理)
标记压缩算法(Mark-Compact)是一种垃圾回收算法,主要用于老年代的垃圾回收。它的基本思想是在标记出所有存活对象之后,将所有存活的对象压缩到内存的一端,将所有空闲的内存空间释放出来,从而减少内存的碎片化。
标记压缩算法与标记-清除算法的区别在于,标记-清除算法只是标记出存活和垃圾对象,并将垃圾对象空间释放出来,因此容易造成内存的碎片化。而标记压缩算法则是将存活对象压缩到一端,并将空闲的内存空间释放出来,解决了内存碎片的问题。

标记压缩过程
标记压缩算法的具体实现过程如下:
标记阶段:从一组 GC Roots 开始,标记出所有存活的对象,并将这些存活对象的标记信息保存在对象头中。
压缩阶段:将所有存活的对象移动到内存的一端,释放出所有空闲的内存空间。在移动对象时,需要更新所有引用对象的指针,确保它们指向对象移动后的位置。
更新指针:由于移动对象的过程中,可能会修改引用对象的指针,因此需要扫描整个堆内存,将所有指向移动过的对象的指针进行更新。(这不算一个阶段)
由于需要更新所有引用对象的指针,因此在多线程情况下,需要使用屏障技术来确保线程安全性。
优点
可以有效地处理不连续的内存空间。
不会产生内存碎片,因为所有存活的对象都被移动到一端。
缺点
压缩阶段需要移动存活的对象,占用了系统的消耗,并且如果标记对象过多的话,损耗可能会很大,会浪费时间和空间。在标记对象相对较少的时候,效率较高。
需要进行两次扫描,第一次标记存活对象,第二次移动存活对象。
为什么没有清除阶段
标记-整理算法确实只有标记和整理两个阶段,没有显式的清除阶段。在整理阶段,垃圾回收器会将所有存活的对象向一端移动,将空间释放到另一端,这样空间的连续段就被重建了。因此,整理阶段的同时也具有清除的功能,所有未被移动的对象都是垃圾,可以直接被视为已经清除了。不过,在实现时,标记-整理算法的整理阶段可能需要把清除的操作分散在整个过程中进行,因此整理阶段通常会和清除阶段结合在一起,实现起来会更加高效。但是从逻辑上来说,标记-整理算法只需要标记和整理两个阶段,没有显式的清除阶段。
复制算法
为了解决效率问题,一种称为“复制”(Copying) 的收集算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存话着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效,算是用空间来换时间的思维。

优点
可以有效地处理大部分对象都是临时对象的情况,例如新生代中的对象。
不会产生内存碎片,因为所有存活的对象都被复制到一个新的内存空间中。
缺点
需要一半的内存空间用于复制存活的对象,会造成空间浪费。
当有大量存活的对象时,复制效率会降低。
4.分代收集算法
目前主流JVM垃圾收集都采用“分代收集”(Generational Collection) 算法,这种算法并没有什么新的思想,只是根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保(如果有空间,通过分配担保机制进入老年代),就必须使用“标记一清理”或者“标记一整理”算法来进行回收。

对于新生代和老年代来说,通常新生代回收的频率很高,但是每次回收的时间都很短,而老年代回收的频率比较低,但是被消耗很多的时间。为了支持高频率的新生代回收,虚拟机可能使用一种叫做卡表(Card table)的数据结构,卡表为一个比特位集合,每一个比特位可以用来表示老年代的某一区域中的所有对象是否持有新生代对象的引用。
这样以来,新生代GC时,可以不用花大量时间扫描所有老年代对象,来确定每一个对象的引用关系,而可以先扫描卡表,只有当卡表的标记为1时,才需要扫描给定区域的老年代对象,而卡表为0的所在区域的老年代对象,一定不含有新生代对象的引用。
卡表中每一位表示老年代4KB的空间,卡表记录为0的老年代区域没有任何对象指向新生代,只有卡表为1的区域才有对象包含新生代对象的引用,因此在新生代GC时,只需要扫面卡表为1所在的老年代空间,使用这种方式,可以大大加快新生代的回收速度。
优点
根据对象的生命周期,将堆分为多个区域,使不同区域采用不同的回收算法,可以提高回收效率。
可以避免全局垃圾回收的情况,从而减少了暂停时间。
缺点
需要维护多个堆区域,增加了复杂性。可能会出现对象晋升的情况,导致老年代的内存压力增大。
5.分区算法
分代算法将按照对象的生命周期长短划分成两个部分,分区算法将整个堆空间划分成不同连续的区域。每个小区间都独立使用,采用不同的垃圾回收算法,独立回收。例如,可以将新生代分成多个区域,每个区域采用不同的复制算法,而老年代则采用标记-整理或标记-清除算法。这种算法的好处是可以控制一次回收多少个小区间。
一般来说,在相同的条件下,堆空间越大,一次GC时所需要的时间就越长,从而产生的停顿也越长。为了更好地控制GC产生的停顿时间,将一块大的内存区域分割为多个小块,根据目标的停顿时间,每次合理的回收若干个小区间,而不是整个堆空间,从而减少一次GC所产生的停顿。这算是一种分治思路。
最后,欢迎大家提问和交流。
如果对你有帮助,欢迎点赞、评论或分享,感谢阅读!
无需注册直接体验ChatGPT!附注册ChatGPT详细教程
2023-02-14

一文掌握,单机Redis、哨兵和Redis Cluster的搭建,建议收藏
2023-02-11

Dubbo重点,SPI的自适应扩展原理|原创
2023-01-11

相关文章:

详解垃圾回收算法,优缺点是什么?|金三银四系列
本文详细介绍了在 JVM 中如何判断哪些对象是需要回收的,以及不同的垃圾回收算法以及优缺点。点击上方“后端开发技术”,选择“设为星标” ,优质资源及时送达上篇文章详细介绍了 JVM 的结构以及其内存结构,需要阅读请移步。本文主要…...
Android 虚拟 A/B 详解(七) SnapshotManager 之标识文件
本文为洛奇看世界(guyongqiangx)原创,转载请注明出处。 原文链接:https://blog.csdn.net/guyongqiangx/article/details/129098176 Android 虚拟 A/B 分区《Android 虚拟 A/B 分区》系列,更新中,文章列表: Android 虚拟 A/B 详解(一) 参考资料推荐Android 虚拟 A/B 详解(二…...
LA@生成子空间@范数@衡量矩阵大小@正交化
文章目录线性组合与线性方程组生成子空间范数LpL^pLp范数向量点积用范数表示ref衡量矩阵大小特殊类型矩阵和向量对角阵向量长度性质单位向量向量单位化(正规化)正交向量正交正交向量组标准正交基正交化(schmidt)正交矩阵矩阵是正交矩阵的充要条件对称矩阵正交相似概念区分&…...

MT2012_竹鼠的白色季节
竹鼠的白色季节 #include<bits/stdc.h> #include<algorithm> using namespace std;/*思路:从小到大排序,然后依次往后遍历即可*/ int main( ) {int n,d;cin>>n>>d; int tmp;vector<int>nums;for(int i0;i<n;i){cin&…...
MySQL是什么?它有什么优势?
随着时间的推移,开源数据库在中低端应用中逐渐流行起来,占据了很大的市场份额。开源数据库具有免费使用、配置简单、稳定性好、性能优良等特点,而 MySQL 数据库正是开源数据库中的杰出代表。 开源全称为“开放源代码”。很多人认为开源软件最…...

基础篇—CSS padding(填充\内边距)解析
CSS padding(填充) CSS padding(填充)是一个简写属性,定义元素边框与元素内容之间的空间,即上下左右的内边距。 属性说明padding使用简写属性设置在一个声明中的所有填充属性padding-bottom设置元素的底部填充padding-left设置元素的左部填充padding-right设置元素的右部…...
二进制枚举
一、左移:用来将一个数的各二进制位全部左移n位,低位以0补充,高位越界后舍弃。n左移1位,n<<1,相当于2*n1左移n位,1<<n,相当于2^n二、右移:将一个数的各二进制位右移N位&…...

2|数据挖掘|聚类分析|k-means/k-均值算法
k-means算法k-means算法,也被称为k-平均或k-均值,是一种得到最广泛应用的聚类算法。算法首先随机选择k个对象,每个对象初始地代表了一个簇的平均值或中心。对剩余的每个对象根据其与各个簇中心的距离,将它赋给最近的簇。然后重新计…...

使用和制作动、静态库
文章目录什么是库?静态库打包方式使用方式生成并执行可执行程序粗暴方式优化方式动态库不一样的.o文件打包方式使用方式生成可执行程序运行可执行程序无法运行时的解决方案动静态库与动静态链接什么是库? 从一开始的helloworld,到现在熟练使…...

【Java基础】023 -- 集合进阶(List、Set、泛型、树)
目录 一、集合的体系结构 1、单列集合(Collection) 二、Collection集合 1、Collection常见方法 ①、代码实现: ②、contains方法重写equals方法示例:(idea可自动重写) 2、Collection的遍历方式(…...

面试题整理01-集合详解
文章目录前言一、集合的整体结构单列集合接口:双列集合接口:二、单列集合详解1.List接口1.1 ArrayList集合特点:扩容:添加元素遍历1.2 LinkedList集合特点:添加元素:2.Set接口2.1 HashSet集合特点ÿ…...

数据驱动的两阶段分布鲁棒(1-范数和∞-范数约束)的电热综合能源系统研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

ArcGIS网络分析之发布网络分析服务(二)
在上一篇中讲述了如何构建网络分析数据集,本篇将讲解如何发布网络分析服务。本文将使用上一篇中建立的网络数据集,下载地址在上一篇博文的最后已给出。 之前我们已经实现了基于ArcMap中的网络分析,但是仅仅支持本地是万万不够的,这里我们的目的就是将我们建好的网络分析图…...

js实现元素样式切换的基本功能
需求:用户第一次点击某些元素,改变元素的某些样式,比如背景颜色,字体颜色。用户第二次点击某些元素,恢复之前的样式。.....思路:准备一定量的div盒子,并取相同的类名<div class"box&quo…...
java 策略模式 + 工厂模式 实例
一 前言 经常听说各种设计模式,知道理论,也知道应该使用,但具体怎么用,什么时候用,使用的优点一直比较模糊,今天写一个项目中经常用到的模式,来具体理解。项目中经常用到工厂模式或者策略模式&…...

本地生成动漫风格 AI 绘画 图像|Stable Diffusion WebUI 的安装和部署教程
Stable Diffusion WebUI 的安装和部署教程1. 简介2. Windows安装环境3. 运行4. 模型下载链接5. 其他资源1. 简介 先放一张WebUI的图片生成效果图,以给大家学习的动力 :) 怎么样,有没有小小的心动?这里再补充一下&…...
华为OD机试 - 异常的打卡记录 | 备考思路,刷题要点,答疑 【新解法】
最近更新的博客 【新解法】华为OD机试 - 关联子串 | 备考思路,刷题要点,答疑,od Base 提供【新解法】华为OD机试 - 停车场最大距离 | 备考思路,刷题要点,答疑,od Base 提供【新解法】华为OD机试 - 任务调度 | 备考思路,刷题要点,答疑,od Base 提供【新解法】华为OD机试…...

「机器学习笔记」之深度学习基础概念(基于Pytorch)
本文以 Pytorch 为线索,介绍人工智能和深度学习相关的一些术语、概念。 关于发展历史您也可以阅读深度学习神经网络之父 Jrgen Schmidhuber 所写的《Annotated History of Modern AI and Deep Learning(现代人工智能和深度学习的注释版历史)…...

概率和似然
在日常生活中,我们经常使用这些术语。但是在统计学和机器学习上下文中使用时,有一个本质的区别。本文将用理论和例子来解释概率和似然之间的关键区别。 概率与似然 假设在一场棒球比赛中,两队的队长都被召集到场上掷硬币。获胜的队长将根据掷…...

前期软件项目评估偏差,如何有效处理?
1、重新评估制定延期计划 需要对项目进行重新评估,将新的评估方案提交项目干系人会议,开会协商一致后按照新的讨论结果制定计划,并实施执行。 软件项目评估偏差 怎么办:重新评估制定延期计划2、申请加资源 如果项目客户要求严格&a…...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...

YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...

OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...

篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...

OPENCV图形计算面积、弧长API讲解(1)
一.OPENCV图形面积、弧长计算的API介绍 之前我们已经把图形轮廓的检测、画框等功能讲解了一遍。那今天我们主要结合轮廓检测的API去计算图形的面积,这些面积可以是矩形、圆形等等。图形面积计算和弧长计算常用于车辆识别、桥梁识别等重要功能,常用的API…...