详解垃圾回收算法,优缺点是什么?|金三银四系列

news2024/9/23 19:27:22

本文详细介绍了在 JVM 中如何判断哪些对象是需要回收的,以及不同的垃圾回收算法以及优缺点。

点击上方“后端开发技术”,选择“设为星标” ,优质资源及时送达

上篇文章详细介绍了 JVM 的结构以及其内存结构,需要阅读请移步。本文主要讲解 JVM 体系中的垃圾收集子系统用到的内存回收算法。

八股文总是忘?一张图牢记JVM内存结构|金三银四系列

2023-02-13

5926b63b143ec579287e6aead989b947.jpeg

哪些对象需要回收

在进行垃圾回收之前,第一件事就是确定哪些对象还存活着,哪些对象已死需要被回收。

1.引用计数算法

很多判断对象是否存活的算法是这样的:

  • 给对象中添加一个引用计数器,每当有个地方引用它时,计数器值就加1;

  • 当引用失效时,计数器值就减1,任何时刻计数器为0的对象就是不可能再被使用的,该对象将被回收。

客观的说,引用计数法实现简单,效率高。但是没有主流JVM选择引用计数法管理内存,因为他无法解决循环引用的问题。如下图e842668b80bb60dface491a0fcdabaab.png

当三个对象都不在使用时,因为相互的引用关系,并不会置为0,所以难以被回收。并且,引用计数器要求在每次因引用产生和消除的时候,伴随一个加法操作和减法操作,对系统性能会有一定的影响。

2.可达性分析算法

主流JVM都是称通过可达性分析来判定对象是否存活的。这个算法的基本思路就是通过一系列的称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链( Reference Chain),当一个对象到GC Roots 没有任何引用链相连,用图论的话来说,就是从GC Roots 到这个对象不可达) 时,则证明此对象是不可用的。

如图所示,深灰色的对象虽然互相有关联,但是它们到 GC Roots 是不可达的,所以它们将会被判定为是可回收的对象。

4270f5a84c017e8428e7fa377680a584.png

GC Root 对象

Java中,有一组GC Roots对象,它们是被垃圾回收器直接管理的对象。GC Roots对象是垃圾回收的起始点,垃圾回收器会从这些对象开始遍历所有的对象,并标记出所有存活的对象,未被标记的对象即为可回收对象。

在Java中,可以作为GC Root对象的包括以下几类:

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象。虚拟机栈中保存着每个线程执行方法时的现场信息,包括局部变量、操作数栈、方法出口等。栈帧中的本地变量表中存放着方法中定义的局部变量和参数,如果局部变量或参数中引用了某个对象,则该对象被认为是一个GC Root对象。

  2. 方法区中类静态属性引用的对象。类似于栈帧中的本地变量表,类中定义的静态属性也会保存引用对象。因为静态属性是属于类的,所以这些引用对象也被认为是GC Root对象。

  3. 方法区中常量引用的对象。常量池中保存着编译器生成的各种字面量和符号引用,例如字符串常量、类和接口的全限定名、字段和方法的名称和描述符等。如果常量池中引用了某个对象,则该对象被认为是GC Root对象。

  4. 本地方法栈中JNI(即Java Native Interface)引用的对象。JNI是Java提供的一种机制,用于调用本地的C/C++代码。如果JNI中引用了某个对象,则该对象被认为是GC Root对象。

需要注意的是,不是所有的对象都可以作为GC Root对象。例如,普通的对象实例和数组元素就不可以作为GC Root对象。只有与Java虚拟机内存管理有关的对象才可以作为GC Root对象,因为它们直接或间接地引用了所有其他对象。

垃圾回收算法

1.标记-清除算法

最基础的收集算法是标记-清除算法(Mark-Sweep),如同它的名字一样,算法分为“标记”和“清除”两个阶段:

  1. 首先标记出所有需要回收的对象(可达性分析)。

  2. 在标记完成后统一回收所有被标记的对象。

之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其不足进行改进而得到的。

ba5f192822c567b9bf2f0b75fa10abe6.png

优点

  • 实现简单,算法简单、容易实现,与保守式GC 算法兼容,有效处理不连续的内存空间

  • 不需要移动对象,因此不会浪费时间和空间。

不足

  • 效率问题:标记和清除两个过程的效率都不高,且需要进行两次扫描,第一次标记垃圾对象,第二次清除垃圾对象。

  • 空间问题:标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作,从而导致频繁的内存分配和回收。

2.标记-压缩算法(标记-整理)

标记压缩算法(Mark-Compact)是一种垃圾回收算法,主要用于老年代的垃圾回收。它的基本思想是在标记出所有存活对象之后,将所有存活的对象压缩到内存的一端,将所有空闲的内存空间释放出来,从而减少内存的碎片化。

标记压缩算法与标记-清除算法的区别在于,标记-清除算法只是标记出存活和垃圾对象,并将垃圾对象空间释放出来,因此容易造成内存的碎片化。而标记压缩算法则是将存活对象压缩到一端,并将空闲的内存空间释放出来,解决了内存碎片的问题。

7c2acabbd043df28537b9daae7e77838.png

标记压缩过程

标记压缩算法的具体实现过程如下:

  • 标记阶段:从一组 GC Roots 开始,标记出所有存活的对象,并将这些存活对象的标记信息保存在对象头中。

  • 压缩阶段:将所有存活的对象移动到内存的一端,释放出所有空闲的内存空间。在移动对象时,需要更新所有引用对象的指针,确保它们指向对象移动后的位置。

  • 更新指针:由于移动对象的过程中,可能会修改引用对象的指针,因此需要扫描整个堆内存,将所有指向移动过的对象的指针进行更新。(这不算一个阶段)

由于需要更新所有引用对象的指针,因此在多线程情况下,需要使用屏障技术来确保线程安全性。

优点

  • 可以有效地处理不连续的内存空间。

  • 不会产生内存碎片,因为所有存活的对象都被移动到一端。

缺点

  • 压缩阶段需要移动存活的对象,占用了系统的消耗,并且如果标记对象过多的话,损耗可能会很大,会浪费时间和空间。在标记对象相对较少的时候,效率较高。

  • 需要进行两次扫描,第一次标记存活对象,第二次移动存活对象。

为什么没有清除阶段

标记-整理算法确实只有标记和整理两个阶段,没有显式的清除阶段。在整理阶段,垃圾回收器会将所有存活的对象向一端移动,将空间释放到另一端,这样空间的连续段就被重建了。因此,整理阶段的同时也具有清除的功能,所有未被移动的对象都是垃圾,可以直接被视为已经清除了。不过,在实现时,标记-整理算法的整理阶段可能需要把清除的操作分散在整个过程中进行,因此整理阶段通常会和清除阶段结合在一起,实现起来会更加高效。但是从逻辑上来说,标记-整理算法只需要标记和整理两个阶段,没有显式的清除阶段。

复制算法

为了解决效率问题,一种称为“复制”(Copying)  的收集算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存话着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效,算是用空间来换时间的思维。

f51ff8b97946e22088ddd24cc8fb8749.png

优点

  • 可以有效地处理大部分对象都是临时对象的情况,例如新生代中的对象。

  • 不会产生内存碎片,因为所有存活的对象都被复制到一个新的内存空间中。

缺点

  • 需要一半的内存空间用于复制存活的对象,会造成空间浪费。

  • 当有大量存活的对象时,复制效率会降低。

4.分代收集算法

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

d6b13e476aeb037d03e5dc38fca39632.png

对于新生代和老年代来说,通常新生代回收的频率很高,但是每次回收的时间都很短,而老年代回收的频率比较低,但是被消耗很多的时间。为了支持高频率的新生代回收,虚拟机可能使用一种叫做卡表(Card table)的数据结构,卡表为一个比特位集合,每一个比特位可以用来表示老年代的某一区域中的所有对象是否持有新生代对象的引用。

这样以来,新生代GC时,可以不用花大量时间扫描所有老年代对象,来确定每一个对象的引用关系,而可以先扫描卡表,只有当卡表的标记为1时,才需要扫描给定区域的老年代对象,而卡表为0的所在区域的老年代对象,一定不含有新生代对象的引用。

卡表中每一位表示老年代4KB的空间,卡表记录为0的老年代区域没有任何对象指向新生代,只有卡表为1的区域才有对象包含新生代对象的引用,因此在新生代GC时,只需要扫面卡表为1所在的老年代空间,使用这种方式,可以大大加快新生代的回收速度。

优点

  • 根据对象的生命周期,将堆分为多个区域,使不同区域采用不同的回收算法,可以提高回收效率。

  • 可以避免全局垃圾回收的情况,从而减少了暂停时间。

缺点

需要维护多个堆区域,增加了复杂性。可能会出现对象晋升的情况,导致老年代的内存压力增大。

5.分区算法

分代算法将按照对象的生命周期长短划分成两个部分,分区算法将整个堆空间划分成不同连续的区域。每个小区间都独立使用,采用不同的垃圾回收算法,独立回收。例如,可以将新生代分成多个区域,每个区域采用不同的复制算法,而老年代则采用标记-整理或标记-清除算法。这种算法的好处是可以控制一次回收多少个小区间。

一般来说,在相同的条件下,堆空间越大,一次GC时所需要的时间就越长,从而产生的停顿也越长。为了更好地控制GC产生的停顿时间,将一块大的内存区域分割为多个小块,根据目标的停顿时间,每次合理的回收若干个小区间,而不是整个堆空间,从而减少一次GC所产生的停顿。这算是一种分治思路。

最后,欢迎大家提问和交流。

如果对你有帮助,欢迎点赞、评论或分享,感谢阅读!

无需注册直接体验ChatGPT!附注册ChatGPT详细教程

2023-02-14

bb054b3cd2787f7fac19b137d8bb9d92.jpeg

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

2023-02-11

43b550370b477d56fd2da031ced758d8.jpeg

Dubbo重点,SPI的自适应扩展原理|原创

2023-01-11

9cf7e6e74a7aa97346ef9c377fd3e601.jpeg

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

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

相关文章

Android 9.0系统源码_通知服务(二)应用发送状态栏通知的流程

前言 应用发送一个显示在状态栏上的通知,对于移动设备来说是很常见的一种功能需求,本篇文章我们将会结合Android9.0系统源码具体来分析一下,应用调用notificationManager触发通知栏通知功能的源码流程。 一、应用触发状态栏通知 应用可以通…

关于HDFS

目录 一、HDFS概述 二、HDFS架构与工作机制 三、HDFS的Shell操作 四、Hdfs的API操作 一、HDFS概述 HDFS:Hadoop Distributed File System;一种分布式文件管理系统,通过目录树定位文件。使用场景:一次写入,多次读出…

java 自定义注解

文章目录前言Annotation包自定义注解自定义注解示例参考文章:java 自定义注解 用处_java注解和自定义注解的简单使用参考文章:java中自定义注解的作用和写法前言 在使用Spring Boot的时候,大量使用注解的语法去替代XML配置文件,十…

SpringAMQP消息队列(SpringBoot集成RabbitMQ)

一、初始配置1、导入maven坐标<!--rabbitmq--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency>2、yml配置spring:rabbitmq:host: 你的rabbitmq的ipport: …

4G模块DTU网关远程抄表方案(三):水表188协议

4G模块DTU网关远程抄表方案&#xff08;三&#xff09;&#xff1a;水气电表188协议 1 CTJ 188协议简介 CJ/T188协议规定了户用计量仪表(以下简称仪表)&#xff0c;包括水表、燃气表、热量表等仪表数据传输的基本原则&#xff0c;接口形式及物理性能、数据链路、数据标识及数…

目标检测回归损失函数简介:SmoothL1/IoU/GIoU/DIoU/CIoU Loss

目标检测 回归损失函数1、Smooth L1 Loss2、 IoU Loss3、 GIoU Loss &#xff08;Generalized-IoU Loss&#xff09;4、 DIoU Loss &#xff08;Distance-IoU Loss&#xff09;5、 CIoU Loss &#xff08;Complete-IoU Loss&#xff09;总结&#xff1a;目标检测任务的损失函数…

【计算机网络】数据链路层(下)

文章目录媒体接入控制媒体接入控制-静态划分信道随机接入 CSMACD协议随机接入 CSMACA协议MAC地址MAC地址作用MAC地址格式MAC地址种类MAC地址的发送顺序单播MAC地址广播MAC地址多播MAC地址随机MAC地址IP地址区分网络编号IP地址与MAC地址的封装位置转发过程中IP地址与MAC地址的变…

1.1 硬件与micropython固件烧录及自编译固件

1.ESP32硬件和固件 淘宝搜ESP32模块,20-50元都有,自带usb口,即插即用. 固件下载地址:MicroPython - Python for microcontrollers 2.烧录方法 为简化入门难度,建议此处先使用带GUI的开发工具THonny,记得不是给你理发的tony老师. 烧录的入口是: 后期通过脚本一次型生成和烧…

[软件工程导论(第六版)]第3章 需求分析(课后习题详解)

文章目录1. 为什么要进行需求分析&#xff1f;通常对软件系统有哪些需求&#xff1f;2. 怎样与用户有效地沟通以获取用户的真实需求&#xff1f;3. 银行计算机储蓄系统的工作过程大致如下&#xff1a;储户填写的存款单或取款单由业务员输入系统&#xff0c;如果是存款则系统记录…

C语言经典编程题100例(81~100)

目录81、习题7-7 字符串替换82、习题8-10 输出学生成绩83、习题8-2 在数组中查找指定元素84、习题8-3 数组循环右移85、题8-9 分类统计各类字符个数86、习题9-2 计算两个复数之积87、习题9-6 按等级统计学生成绩88、习题11-1 输出月份英文名89、习题11-2 查找星期90、练习10-1 …

分享113个HTML娱乐休闲模板,总有一款适合您

分享113个HTML娱乐休闲模板&#xff0c;总有一款适合您 113个HTML娱乐休闲模板下载链接&#xff1a;https://pan.baidu.com/s/1aWYO2j2pSTjyqlQPHa0-Jw?pwdbium 提取码&#xff1a;bium Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 海上的沤鸟HTML网页模板…

(三十六)Vue解决Ajax跨域问题

文章目录环境准备vue的跨域问题vue跨域问题解决方案方式一方式二上一篇&#xff1a;&#xff08;三十五&#xff09;Vue之过渡与动画 环境准备 首先我们要借助axios发送Ajax&#xff0c;axios安装命令&#xff1a;npm i axios 其次准备两台服务器&#xff0c;这里使用node.j…

Linux | 网络通信 | 序列化和反序列化的讲解与实现

文章目录为什么要序列化&#xff1f;协议的实现服务端与客户端代码实现为什么要序列化&#xff1f; 由于默认对齐数的不同&#xff0c;不同的平台对相同数据进行内存对齐后&#xff0c;可能得到不同的数据。如果直接将这些数据进行网络传输&#xff0c;对方很可能无法正确的获…

【数据结构】单链表的接口实现(附图解和源码)

单链表的接口实现&#xff08;附图解和源码&#xff09; 文章目录单链表的接口实现&#xff08;附图解和源码&#xff09;前言一、定义结构体二、接口实现&#xff08;附图解源码&#xff09;1.开辟新空间2.头插数据3.头删数据4.打印整个单链表5.尾删数据6.查找单链表中的数据7…

Linux 磁盘挂载

目录 Linux硬盘分区 硬盘设备的文件名 /dev/sd[a-z] 硬盘分区 识别硬盘的文件名 Linux文件系统 文件系统类型 Linux如何保存文件 VFS虚拟文件系统 磁盘挂载命令 lsblk 查看系统的磁盘使用情况 fdisk 硬盘分区 mkfs 格式化文件系统 mount 挂载命令 df 显示磁盘空间…

Java中的链表实现介绍

Java中的链表实现介绍 学习数据结构的的链表和树时&#xff0c;会遇到节点&#xff08;node&#xff09;和链表&#xff08;linked list&#xff09;这两个术语&#xff0c;节点是处理数据结构的链表和树的基础。节点是一种数据元素&#xff0c;包括两个部分&#xff1a;一个是…

pytest总结

这里写目录标题一、pytest的命名规则二、界面化配置符合命名规则的方法前面会有运行标记三、pytest的用例结构三部分组成四、pytest的用例断言断言写法&#xff1a;五、pytest测试框架结构六、pytest参数化用例1、pytest参数化实现方式2、单参数&#xff1a;每一条测试数据都会…

第五十七章 树状数组(二)

第五十七章 树状数组&#xff08;二&#xff09;一、差分的缺陷二、树状数组与差分三、例题题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示样例 1 解释&#xff1a;数据规模与约定代码一、差分的缺陷 差分的作用是能够在O(1)的时间内给一段区间加上相同的数字&am…

【计算机网络】数据链路层(上)

文章目录数据链路层概述封装成帧透明传输差错检测奇偶校验循环冗余校验CRC可靠传输可靠传输基本概念实现机制 — 停止-等待协议实现机制 — 回退N帧协议实现机制 — 选择重传协议点对点协议PPP数据链路层概述 首先我蛮来看看数据链路层在网络体系结构中的地位。如图所示主机h1…

key的作用原理与列表的遍历、追加、搜索、排序

目录 一、key的作用原理 二、实现列表遍历并对在列表最前方进行追加元素 三、实现列表过滤搜索 1、用computed计算属性来实现 2、用watch监听输入值的变化来实现 四、按年龄排序输出列表 一、key的作用原理 1. 虚拟DOM中key的作用&#xff1a; key是虚拟DOM对象的标识&a…