垃圾回收机制和常用的算法

news2024/12/30 1:37:10

一.什么是垃圾回收?

垃圾回收主要针对堆和方法区(非堆),程序计数器,虚拟机栈,本地方法栈这三个区域属于线程私有,随着线程的销毁,自然就会雄安会了,因此不需要堆着三个区域进行垃圾回收。

二.如何判断一个对象是否可以被回收

堆中几乎放着所有的对象实例(为什么这么说呢?因为现在有了JIT和标量替换等优化方法),对对垃圾回收前的第一步就是判断哪些对象已经死亡,即不能再被任何途径使用的对象。

引用计数算法

为对象添加一个引用计数器,当对象增加一个引用计数器就加一,引用失效就减一,引用计数为0的对象可被回收,在两个对象出现循环引用的情况下,此时引用计数器永远不会为0,导致无法对他们及逆行回收,就是因为有循环引用的存在,因此java虚拟机不使用引用计数算法。

public class Test {

    public Object instance = null;

    public static void main(String[] args) {
        Test a = new Test();
        Test b = new Test();
        a.instance = b;
        b.instance = a;
        a = null;
        b = null;
        doSomething();
    }
}

上面的例子中,a,b引用对象示例互相持有了对象的引用,因此当我们把对a对象和b对象的引用去除之后,由于两个对象还存在互相引用,导致两个Test对象无法被回收

优点 执行效率高,缺点,无法解决循环引用,引起内存泄漏

可达性分析算法

通过判断对象的引用链是否可达来决定对象是否可以被回收

以GC roots 为起始点进行搜索,可达的对象都是存货的,不可达的对象可被回收?

Java 虚拟机使用可达性分析算法来判断对象是否可被回收,GC Roots 一般包含以下几种:

  • 虚拟机栈中局部变量表中引用的对象(栈帧中的本地方法变量表)

  • 本地方法栈中 JNI(Native方法) 中引用的对象

  • 方法区中类静态属性引用的对象

  • 方法区中的常量引用的对象

  • 活跃线程的引用对象

方法区的回收

因为方法区主要存放永久代对象,而永久代对象的回收率比新生代低很多,所以在方法区上进行回收性价比不高

主要对常量池的回收和对类的卸载

为了避免内存溢出,在大量使用反射和动态代理的场景都需要虚拟机具备类卸载功能。

  • 类卸载条件很多,需要满足以下三个条件,此时堆中不存在该类的任何示例。
  • 加载该类的ClassLoader已经被回收
  • 该类对应的Class对象没有任何地方引用了,也就是无法在任何地方通过反射访问该类的方法

finalize() 

用于关闭外部资源。但是 try-finally 等方式可以做得更好,并且该方法运行代价很高,不确定性大,无法保证各个对象的调用顺序,因此最好不要使用。

当垃圾回收器宣告一个对象死亡时,至少要经理两次标记过程

如果对象在进行可达性分析以后,没有与GC root 直接相连接的引用,就会被第一次标记。并且判断是否执行finalize方法;

如果这个对象覆盖了finalize() 并且未被引用,就会放置F-Queue对象,稍后由虚拟机创建一个低优先级的finalize()线程去执行触发finalize()方法,在该方法中让对象重新被引用,从而实现自救,但是该线程的优先级比较低,执行过程随时可能被终止,此外,自救只能进行一次,如果回收的对象之前调用了 finalize() 方法自救,后面回收时不会再调用该方法。

强引用

被强引用关联的对象不会被回收。

使用 new 一个新对象的方式来创建强引用。

当内存空间不足,JVM 抛出 OOM Error 终止程序也不会回收具有强引用的对象,只有通过将对象设置为 null 来弱化引用,才能使其被回收。

软引用

表示对象处在有用但非必须的状态。

被软引用关联的对象只有在内存不够的情况下才会被回收。可以用来实现内存敏感的高速缓存。

软引用可以和一个引用队列 ReferenceQueue 联合使用,如果软引用所引用的对象被垃圾回收,JVM 就会把这个软引用加入到与之关联的引用队列中。如果一个弱引用对象本身在引用队列中,就说明该引用对象所指向的对象被回收了。

使用 SoftReference 类来创建软引用。

弱引用

表示非必须的对象,比软引用更弱一些。适用于偶尔被使用且不影响垃圾收集的对象。

被弱引用关联的对象一定会被回收,也就是说它只能存活到下一次垃圾回收发生之前。

弱引用可以和一个引用队列 ReferenceQueue 联合使用,如果弱引用所引用的对象被垃圾回收,JVM 就会把这个弱引用加入到与之关联的引用队列中。如果一个弱引用对象本身在引用队列中,就说明该引用对象所指向的对象被回收了。

使用 WeakReference 类来创建弱引用。

虚引用

又称为幽灵引用或者幻影引用,一个对象是否有虚引用的存在,不会对其生存时间造成影响,也无法通过虚引用得到一个对象。

不会决定对象的生命周期,任何时候都可能被垃圾回收器回收。必须和引用队列 ReferenceQueue 联合使用

为一个对象设置虚引用的唯一目的是能在这个对象被回收时收到一个系统通知,起哨兵作用。具体来说,就是通过判断引用队列 ReferenceQueue 是否加入虚引用来判断被引用对象是否被 GC(垃圾回收线程) 回收:当 GC 准备回收一个对象时,如果发现它还仅有虚引用指向它,就会在回收该对象之前,把这个虚引用加入到与之关联的引用队列 ReferenceQueue 中。如果一个虚引用对象本身就在引用队列中,就说明该引用对象所指向的对象被回收了

使用 PhantomReference 来创建虚引用。

引用类型被垃圾回收的时间用途生存时间
强引用从来不会对象的一般状态JVM停止运行时终止
软引用在内存不足的时候对象缓存内存不足时终止
弱引用在垃圾回收的时候对象缓存GC运行后终止
虚引用Unknown标记、哨兵Unknown

垃圾回收算法

1.标记清除算法

 标记阶段,从根集合进行扫描,会检查每个对象是否为活动对象,如果是活动对象,则程序会在对象头打上标记

清除阶段,会进行对象回收并取消标志位,另外还会帕努但回收后的分块与前一个空闲分块是否连续,如果连续会合并这两个分块,回收对象就是把对象作为分块,连接到被称为空闲链表的单向链表,之后进行分配时只需要遍历这个空闲链表,就可以找到分块。

分配阶段,程序会搜索空闲链表寻找空间大于等于新对象大小size的块block,如果它找到的块等于size 会直接返回这个分块,如果找到大于size,就会对块分割成大小为size 与block-size的两部分,返回大小为size的分块,并把大小为(block - size) 的块返回给空闲链表。

不足:

  • 标记和清除过程效率都不高;

  • 会产生大量不连续的内存碎片,导致无法给大对象分配内存。

2.标记整理算法

‘ 

标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

优点:不会产生内存碎片

不足:需要移动大量对象,处理效率比较低。

复制算法

 

将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。

主要不足是只使用了内存的一半。

现在的商业虚拟机都采用这种收集算法回收新生代,但是并不是划分为大小相等的两块,而是一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor。在回收时,将 Eden 和 Survivor 中还存活着的对象全部复制到另一块 Survivor 上,最后清理 Eden 和使用过的那一块 Survivor。

HotSpot 虚拟机的 Eden 和 Survivor 大小比例默认为 8:1,保证了内存的利用率达到 90%。如果每次回收有多于 10% 的对象存活,那么一块 Survivor 就不够用了,此时需要依赖于老年代进行空间分配担保,也就是借用老年代的空间存储放不下的对象

分代收集

现在的商业虚拟机采用分代收集算法,它根据对象存活周期将内存划分为几块,不同块采用适当的收集算法。

一般将堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法:

  • 新生代:新生代对象存活时间很短,所以可以选择“复制”算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。

  • 老年代:老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。

Stop-the-World & SafePoint

Stop-the-World

所谓 Stop-the-World(简称 STW),指的是 JVM 由于要执行 GC 而停止了应用程序的执行 :

  • 可达性分析算法中 GC Roots 会导致所有 Java 执行线程停顿,原因如下:

    • 分析工作必须在一一个能确保一致性的快照中进行

    • 一致性指整个分析期间整个执行系统看起来像被冻结在某个时间点上

    • 如果出现分析过程中对象引用关系还在不断变化,则分析结果的准确性无法保证

  • 被 STW 中断的应用程序线程会在完成 GC 之后恢复,频繁中断会让用户感觉像是网速不快造成电影卡带一样, 影响用户体验,所以需要减少 STW 的发生。

STW 事件和采用哪款垃圾收集器无关,所有的 GC 都有这个事件。哪怕是 G1 也不能完全避免 STW 情况发生,只能说垃圾回收器越来越优秀,回收效率越来越高,尽可能地缩短了暂停时间。

STW 是 JVM 在后台自动发起和自动完成的。在用户不可见的情况下,把用户正常的工作线程全部停掉。开发中不要用 System.gc()  这样会导致 STW 的发生。

目前,降低系统的停顿时间两种算法:增量收集算法和分区算法。

增量收集算法

基本思想:如果一次性将所有的垃圾进行处理,需要造成系统长时间的停顿,那么就可以让垃圾收集线程和应用程序线程交替执行。垃圾收集线程一次只收集一小片区域的内存空间,接着切换到应用程序线程。依次反复,直到垃圾收集完成。总的来说,增量收集算法的基础仍是传统的标记-清除和复制算法。增量收集算法通过对线程间冲突的妥善处理,允许垃圾收集线程以分阶段的方式完成标记、清理或复制工作。

不足:由于在垃圾回收过程中,间断性地还执行了应用程序代码,所以能减少系统的停顿时间。但是因为线程切换和上下文转换的消耗,会使得垃圾回收的总体成本上升,造成系统吞吐量的下降。

分区算法

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

注意分区算法与分代收集算法是不同的:分代收集算法将按照对象的生命周期长短划分成两个部分,而分区算法将整个堆空间划分成连续的不同小区间,其中每个小区间都独立使用,独立回收,这样可以控制一次回收多少个小区间。

总结:

  • 增量收集算法是将总的收集量一部分一部分的去执行

  • 分区算法是将总的内存空间分为小分区,一次可控的去收集多少个小区间。

SafePoint

程序执行时并非可以在任何地方都能停顿下来开始 GC,只有在特定的位置才能停顿下来开始 GC,这些位置称为Safepoint 。

SafePoint 的选择很重要,如果太少可能导致 GC 等待的时间太长,如果太频繁可能导致运行时的性能问题。

大部分指令的执行时间都非常短暂,通常会根据是否具有让程序长时间执行的特征为标准。比如选择一些执行时间较长的指令作为 SafePoint,如方法调用、循环跳转和异常跳转等。

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

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

相关文章

114.(cesium篇)cesium去掉时间轴并用按钮控制运动

地图之家总目录(订阅之前必须详细了解该博客) 地图之家:cesium+leaflet+echart+地图数据+地图工具等相关内容的介绍 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 效果如下所示: cesium去掉时间轴并用按钮控制运动 下面献上完整代码,代码重要位…

剔除多余括号 C++详解

剔除多余括号 C详解 题目描述输入输出样例输入样例输出样例 解法&代码 题目描述 剔除四则运算表达式中的多余括号。 输入一个含有括号的四则运算表达式,可能含有多余的括号,编程整理该表达式,去掉所有多余的括号,原表达式中所…

管理类联考——逻辑——形式逻辑——汇总篇

简述 形式逻辑: 识别题型:逻辑符号表达及标志词:联假言符号化特殊命题“除非否则”;五大关系:矛盾、等价、包含、至少有一真、至少有一假;【通过“关系”,串联起“假联选”言】 识别题型&…

《吐血整理》高级系列教程-吃透Fiddler抓包教程(23)-Fiddler如何优雅地在正式和测试环境来回切换-上篇

1.简介 在开发或者测试的过程中,由于项目环境比较多,往往需要来来回回地反复切换,那么如何优雅地切换呢?今天介绍几种方法供小伙伴或者童鞋们进行参考。 2.实际工作场景 2.1问题场景 (1)已发布线上APP出…

1-3 AUTOSAR标准化接口

AUTOSAR标准化接口 AUTOSAR规范中,将不同模块间通信的接口主要分为以下三类: 标准接口(Standardized Interface) 在AUTOSAR规范中以C语言中API的形式明确定义。主要用于ECU上的BSW各模块间、RTE和操作系统间、RTE和通信模块间&am…

页面技术基础-html

页面技术基础-html 环境准备:在JDBC中项目上完成代码定义 1. 新建一个 Module:filr->右键 -》Module -》Java-》next->名字(html_day1)->finish 2. 在 Moudle上右键-》第二个选项:add framework .. -> 选择JavaEE下第一个选项 Web Apllicat…

Pytorch深度学习-----神经网络之线性层用法

系列文章目录 PyTorch深度学习——Anaconda和PyTorch安装 Pytorch深度学习-----数据模块Dataset类 Pytorch深度学习------TensorBoard的使用 Pytorch深度学习------Torchvision中Transforms的使用(ToTensor,Normalize,Resize ,Co…

[JavaScript游戏开发] 绘制Q版地图、键盘上下左右地图场景切换

系列文章目录 第一章 2D二维地图绘制、人物移动、障碍检测 第二章 跟随人物二维动态地图绘制、自动寻径、小地图显示(人物红点显示) 第三章 绘制冰宫宝藏地图、人物鼠标点击移动、障碍检测 第四章 绘制Q版地图、键盘上下左右地图场景切换 文章目录 系列文章目录前言一、本章节…

Spring?Boot项目如何优雅实现Excel导入与导出功能

目录 背景EasyExcel 问题分析与解决Spring Boot Excel 导入与导出 依赖引入Excel 导入 基本导入功能进阶导入功能Excel 导出 Excel 导入参数校验 开启校验 校验规则定义 Bean Validation 定义校验规则ExcelValidator 接口定义校验规则校验结果接收 异常捕获接收校验结果contro…

无涯教程-Lua - 垃圾回收

Lua使用自动内存管理,该管理使用基于Lua内置的某些算法的垃圾回收。 垃圾收集器暂停 垃圾收集器暂停用于控制垃圾收集器之前需要等待多长时间; Lua的自动内存管理再次调用它。值小于100意味着Lua将不等待下一个周期。同样,此值的较高值将导…

《吐血整理》高级系列教程-吃透Fiddler抓包教程(21)-如何使用Fiddler生成Jmeter脚本-上篇

1.简介 我们知道Jmeter本身可以录制脚本,也可以通过BadBoy,BlazeMeter等工具进行录制,其实Fiddler也可以录制Jmter脚本(而且有些页面,由于安全设置等原因,使用Jmeter直接无法打开录制时,这时就…

PyQT模块、类、控件介绍

最近在搞一些基于PyQT的开发,开发过程中一直对PyQT相关模块、类、控件比较模糊,于是花了一些力气,去收集和整理了一下PyQT的一些基础,希望对大家有帮助! PyQT模块 QtCore模块涵盖了包的核心的非GUI功能,此模…

windows 安装 Mysql 5.7 和8.0

下载链接 https://dev.mysql.com/downloads/mysql/

【echarts饼图】legend显示data中的name和value

效果图: legend自定义显示格式: legend: {formatter: function (name) {let v;optionCheck.series.data.forEach((item) > {if (item.name name) {v item.value;}});return name v;},},全部配置项: const optionCheck reactive(…

Shell脚本学习-MySQL单实例和多实例启动脚本

已知MySQL多实例启动命令为: mysqld_safe --defaults-file/data/3306/my.cnf & 停止命令为: mysqladmin -uroot -pchang123 -S /data/3306/mysql.sock shutdown 请完成mysql多实例的启动脚本的编写: 问题分析: 要想写出脚…

BES 平台 SDK之提示音的添加

本文章是基于BES2700 芯片,其他BESxxx 芯片可做参考,如有不当之处,欢迎评论区留言指出。仅供参考学习用! BES 平台 SDK之按键的配置_谢文浩的博客-CSDN博客 关于系统按键简介可参考上一篇文章。链接如上所示! 一&am…

工作流管理软件的好处:提升效率、优化流程的利器

一旦您投资了工作流管理系统,就没有回头路了。这是让您的员工满意的完美公式,同时确保所有流程以最高效率和及时完成。事实证明,这些实用的工作流程管理工具对为其客户提供基于知识的专业服务的组织有益(在我们的专业服务指南中了…

【3维视觉】3D空间常用算法(点到直线距离、面法线、二面角)

3D空间点到直线的距离 3D空间点到直线的距离 3D空间的曲率 三维空间有三个基本元素,点,线,面。那么曲率是如何定义的呢? 点的曲率? 线的曲率? 面的曲率? 法曲率 设曲面上的曲线在某一点处的切…

【Spring Boot】请求参数传json数组,后端采用(pojo)新增案例(103)

请求参数传json数组,后端采用(pojo)接收的前提条件: 1.pom.xml文件加入坐标依赖:jackson-databind 2.Spring Boot 的启动类加注解:EnableWebMvc 3.Spring Boot 的Controller接受参数采用:Reque…

第一个 vue-cli 项目

一、什么是 vue-cli vue-cli 官方提供的一个脚手架,用于快速生成一个 vue 的项目模板;预先定义好的目录结构及基础代码,就好比咱们在创建 Maven 项目时可以选择创建一个骨架项目,这个骨架项目就是脚手架,我们的开发更加…