垃圾回收机制及算法

文章目录

    • 概要
    • 对象存活判断
      • 引用计数算法
      • 可达性分析算法
      • 对象是否存活
      • 各种引用
    • 垃圾收集算法
      • 分代收集理论
      • 复制算法
      • 标记清除算法
      • 标记-整理算法

概要

垃圾收集(Garbage Collection, 下文简称GC),其优缺点如下:
优点:

  • 不需要考虑内存管理,
  • 可以有效的防止内存泄漏,有效的利用可使用的内存,

缺点:

  • java开发人员不了解自动内存管理, 内存管理就像一个黑匣子,过度依赖就会降低我们解决内存溢出/内存泄漏等问题的能力。

对象存活判断

引用计数算法

引用计数算法可以这样实现:给每个创建的对象添加一个引用计数器,每当此对象被某个地方引用时,计数值+1,引用失效时-1,所以当计数值为0时表示对象已经不能被使用。引用计数算法大多数情况下是个比较不错的算法,简单直接,也有一些著名的应用案例但是对于Java虚拟机来说,并不是一个好的选择,因为它很难解决对象直接相互循环引用的问题。

优点:
实现简单,执行效率高,很好的和程序交织。
缺点:
无法检测出循环引用。

可达性分析算法

在主流的商用程序语言如Java、C#等的主流实现中,都是通过可达性分析(Reachability Analysis)来判断对象是否存活的。此算法的基本思路就是通过一系列的“GC Roots”的对象作为起始点,从起始点开始向下搜索到对象的路径。搜索所经过的路径称为引用链(Reference Chain),当一个对象到任何GC Roots都没有引用链时,则表明对象“不可达”,即该对象是不可用的。

在这里插入图片描述

在Java语言中,可作为GC Roots的对象包括下面几种:

  • 栈帧中的局部变量表中的reference引用所引用的对象
  • 方法区中static静态引用的对象
  • 方法区中final常量引用的对象
  • 本地方法栈中JNI(Native方法)引用的对象
  • Java虚拟机内部的引用, 如基本数据类型对应的Class对象, 一些常驻的异常对象(比如 NullPointExcepiton、
    OutOfMemoryError) 等, 还有系统类加载器。
  • 所有被同步锁(synchronized关键字) 持有的对象
  • 反映Java虚拟机内部情况的JMXBean、 JVMTI中注册的回调、 本地代码缓存等

在这里插入图片描述

对象是否存活

finalize()方法最终判定对象是否存活:

即使在可达性分析算法中判定为不可达的对象, 也不是“非死不可”的, 这时候它们暂时还处于“缓刑”阶段, 要真
正宣告一个对象死亡, 至少要经历两次标记过程:

第一次标记:
如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链, 那它将会被第一次标记, 随后进行一次筛选, 筛选的条件是此对象是否有必要执行finalize()方法。
没有必要:
假如对象没有覆盖finalize()方法, 或者finalize()方法已经被虚拟机调用过, 那么虚拟机将这两种情况都视为“没有必要执行”。
有必要:

如果这个对象被判定为确有必要执行finalize()方法, 那么该对象将会被放置在一个名为F-Queue的 队列之中, 并在
稍后由一条由虚拟机自动建立的、 低调度优先级的Finalizer线程去执行它们的finalize() 方法。 finalize()方法是对 象
逃脱死亡命运的最后一次机会, 稍后收集器将对F-Queue中的对象进行第二次小规模的标记, 如果对 象要在
finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可, 譬如把自己 (this关键字) 赋值
给某个类变量或者对象的成员变量, 那在第二次标记时它将被移出“即将回收”的集 合; 如果对象这时候还没有逃
脱, 那基本上它就真的要被回收了。

在这里插入图片描述

Finalizer线程去执行它们的finalize() 方法, 这里所说的“执行”是指虚拟机会触发这个方法开始运行, 但并不承诺一定会等待它运行结束。 这样做的原因是, 如果某个对象的finalize()方法执行缓慢, 或者更极端地发生了死循环, 将很可能导 致F-Queue队列中的其他对象永久处于等待, 甚至导致整个内存回收子系统的崩溃。

各种引用

在JDK1.2之后,Java对引用的概念做了扩充,将引用分为 强引用(Strong Reference) 、 软引用(Soft Reference) 、 弱引用(Weak Reference) 和 虚引用(Phantom Reference) 四种,这四种引用的强度依次递减。

  • 强引用:是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟
    机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问
    题。 ps:强引用其实也就是我们平时A a = new A()这个意思。

  • 软引用: 如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。 软引用可以和一个引用队列
    (ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

  • 弱引用:用来描述那些非必须对象。 当垃圾收集器开始工作, 无论当前内存是否足够, 都会回收掉只被弱引用关联的对象。 在JDK 1.2版之后提供了WeakReference类来实现弱引用。 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中.

  • 虚引用:一个对象仅持有虚引用,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动
    虚引用与软引用和弱引用的一个区别在于:
    1、引用必须和引用队列 (ReferenceQueue)联合使用。
    2、当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到
    与之 关联的引用队列中。

垃圾收集算法

分代收集理论

当前商业虚拟机的垃圾回收器,大多遵循“分代收集”的理论来进行设计,这个理论大体上是这么描述的:

  • 绝大部分的对象都是朝生夕死。
  • 熬过多次垃圾回收的对象就越难回收
  • 跨代引用相对于同代引用来说仅仅占少数

根据以上两个理论,朝生夕死的对象放一个区域,难回收的对象放另外一个区域,这个就构成了新生代和老年代,并且不同的分代采用的回收算法不一样。

在这里插入图片描述

对于第3个理论,这里补充说明以下。

分代收集也并不是简单划分一下内存区域这么简单,因为对象不是孤立的,对象之间存在跨代引用,譬如:现在要在新生代进行回收,但新生代的对象极有可能被老年代对象所引用,那为了找到这些可能存活的对象,不得不在既定的 GC Roots 之外,再遍历整个老年代对象确保可达性分析结果的正确性。反过来回收老年代也是一样。

但是这样无疑带来了性能负担,为了解决这个问题,才有了第3条理论;那如何解决跨代引用呢?
正是因为只占少数,所以不应该为了为了这些少量的跨代引用而区扫描整个老年代,也不能浪费空间让每个对象都记录它是否存在跨代引用,所以为了解决这个问题只需要在新生代建立一个全局的数据结构叫做:记忆集( Remembered Set ),这个结构把老年代划分成若干小块,并标识哪块内存存在跨代引用,后续新生代发生 gc 时,只有包含了跨代引用的小内存区域才会被加入到 GC Roots 进行扫描;当然这种方法需要在对象改变引用关系的时候维护记忆集中数据的正确性。这种做法相比垃圾收集时扫描整个老年代来说仍然时划算的。

同时对于GC的叫法,大体有这么几种:
1、新生代回收( Minor GC/Young GC ):指的是进行新生代的回收。
2、老年代回收( Major GC/Old GC ):指的是进行老年代的回收。目前只有 CMS 垃圾回收器会有这个单独的回收老年代的行为。( Major GC 定义相对没有那么明确,有说指是老年代,有的说是做整个堆的收集,没有固定的说法,有时候 Major GC 和 Full GC 大致是等价的)
3、整堆回收( Full GC ):定义相对明确,收集整个 Java 堆和方法区(注意包含方法区)

复制算法

原始的复制算法(Copying)是这样的:

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

在这里插入图片描述

优点:

  • 实现简单,运行高效,
  • 每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要按顺序分配内存即可

缺点:

  • 内存的使用率缩小为原来的一半。
  • 内存移动是必须实打实的移动(复制),所以对应的引用(直接指针)需要调整。

适用场景:
复制回收算法适合于新生代,因为大部分对象朝生夕死,那么复制过去的对象比较少,效率自然就高,另外一半的一次性清理是很快的。

像 hotspot 这样的虚拟机大都对原生的复制算法进行了改进,因为它对内存空间的利用率不高,而且专门研究表明,新生代中的对象 98% 是“朝生夕死”的,所以并不需要按照 1:1 的比例来划分内存空间,所以改进后的复制算法策略是:

1、将新生代划分为一块较大的 Eden 区和两块较小的 Survivor 空间(你可以叫做 From 或者 To ) , HotSpot 虚拟机默认 Eden 和 Survivor 的大小比例是 8:1 。
2、每次使用 Eden 和其中一块 Survivor ,当回收时,将 Eden 和Survivor 中还存活着的对象一次性地复制到另外一块 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor 空间。

在这样的算法下,
1、每次新生代中可用内存空间为整个新生代容量的 90%(80%+10%),只有 10%的内存会被 “浪费”
2、当然,98%的对象可回收只是一般场景下的数据,我们没有办法保证每次回收都只有不多于 10%的对象存活,当 Survivor 空间不够用时,需要依赖其他内存(老年代)进行分配担保( Handle Promotion )。

标记清除算法

标记-清除(Mark-Sweep)算法分为“标记”和“清除”两个阶段:

  • 首先扫描所有对象标记出需要回收的对象
  • 在标记完成后扫描并回收所有被标记的对象,故需要两次扫描

在这里插入图片描述
注意:
1、回收效率略低,如果大部分对象是朝生夕死,那么回收效率降低,因为需要大量标记对象和回收对象,对比复制回收效率要低,所以该算法不适合新生代。
2、它的主要问题是在标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾回收动作。
3、标记清除算法适用于老年代。

标记-整理算法

标记-整理(Mark-Compact)算法逻辑如下:
1、首先标记出所有需要回收的对象,
2、在标记完成后,后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,
3、然后直接清理掉端边界以外的内存。

在这里插入图片描述

注意:
1、标记整理需要扫描两遍
2、标记整理与标记清除算法的区别主要在于对象的移动。对象移动不单单会加重系统负担,同时需要全程暂停用户线程才能进行,同时所有引用对象的地方都需要更新(直接指针需要调整)。
3、标记整理算法不会产生内存碎片,但是效率偏低。
4、标记整理算法适用于老年代。

所以看到,老年代采用的标记整理算法与标记清除算法,各有优点,各有缺点。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java+IDEA+SpringBoot药物不良反应ADR智能监测系统源码 ADR智能化监测系统源码

JavaIDEASpringBoot药物不良反应ADR智能监测系统源码 ADR智能化监测系统源码 药物不良反应(Adverse Drug Reaction,ADR)是指在使用合格药品时,在正常的用法和用量下出现的与用药目的无关的有害反应。这些反应往往因药物种类、使用…

韩愈,文起八代之衰的儒学巨匠

💡 如果想阅读最新的文章,或者有技术问题需要交流和沟通,可搜索并关注微信公众号“希望睿智”。 韩愈,字退之,生于唐代宗大历三年(公元768年),卒于唐穆宗长庆四年(公元82…

LangChain 0.2 - 对话式RAG

文章目录 一、项目说明二、设置1、引入依赖2、LangSmith 三、Chains1、添加聊天记录Contextualizing the question聊天记录状态管理 2、合并 四、Agents1、检索工具2、代理建造者3、合并 五、下一步 本文翻译整理自:Conversational RAG https://python.langchain.co…

spring suite gitlab使用手册

一、gitlab介绍 GitLab是一个功能丰富的开源代码管理平台,基于Git进行版本控制,并提供了一系列用于团队协作、项目管理、持续集成/持续部署(CI/CD)等工具。以下是关于GitLab的详细介绍: 基础信息: GitLab…

LiveGBS流媒体平台GB/T28181用户手册-云端录像:查看录像、列表视图、时间轴视图、下载、删除

LiveGBS流媒体平台GB/T28181用户手册-云端录像:查看录像、列表视图、时间轴视图、下载、删除 1、云端录像1.1、查看录像1.1.1、时间轴视图1.1.2、列表视图1.1.3、日期切换1.1.4、删除当天 1.2、录像计划1.2.1、录像计划列表1.2.2、编辑录像计划1.2.3、关联通道1.2.4、删除录像计…

每日练习之——背包问题

完全背包 题目描述 运行代码 #include<bits/stdc.h> #include<iostream> using namespace std; const int N1e33; int n,V; int v[N],w[N],dp[N]; int main(){cin>>n>>V; int t1;while(t--){for(int i1;i<n;i){cin>>v[i]>>w[i];}mems…

极简编程:一行JS代码获取全球各城市当前时间!

之前在一些国际化网站看到过&#xff0c;他们展示了当前北京、纽约和伦敦的时钟&#xff0c;在一次住店的时候&#xff0c;我也看到了类似的3个时钟&#xff0c;甚至更多&#xff0c;有的会展示东京时间。 让我觉得获取一些全球重点城市的当前时间&#xff0c;会是一个很常用的…

OrangePi Kunpeng Pro开发板初体验——家庭小型服务器

引言 在开源硬件的浪潮中&#xff0c;开发板作为创新的基石&#xff0c;正吸引着全球开发者的目光。它们不仅为技术爱好者提供了实验的平台&#xff0c;更为专业开发者带来了实现复杂项目的可能性。本文将深入剖析OrangePi Kunpeng Pro开发板&#xff0c;从开箱到实际应用&…

Bootstrap 3.x 版本基础引入指南

Bootstrap 是一款广受欢迎的前端框架&#xff0c;它简化了网页设计与开发流程&#xff0c;帮助开发者快速创建响应式布局和美观的网页界面。本文将向您介绍如何在项目中引入 Bootstrap 3.x 版本的基本步骤&#xff0c;包括 CSS 和 JavaScript 文件的引用&#xff0c;以及必要的…

PyTorch的数据处理

&#x1f4a5;今天看一下 PyTorch数据通常的处理方法~ 一般我们会将dataset用来封装自己的数据集&#xff0c;dataloader用于读取数据 Dataset格式说明 &#x1f4ac;dataset定义了这个数据集的总长度&#xff0c;以及会返回哪些参数&#xff0c;模板&#xff1a; from tor…

国内信创数据库生态

国内信创数据库生态 国内信创数据库主要包括但不限于以下几种&#xff1a; 数据库类型与厂商&#xff1a; 达梦 &#xff08;武汉达梦&#xff09; 官网 https://www.dameng.com/DM8.html 人大金仓 &#xff08;北京&#xff09; 官网 https://www.kingbase.com.cn/tyxsjk/i…

Linux如何设置共享文件夹

打开虚拟机->菜单->虚拟机设置->选项->共享文件夹->总是启用。点击添加按钮->弹出添加向导->点击浏览按钮&#xff0c;从windows中选择一个文件夹&#xff0c;确定即可。

STM32_RCC

1、RCC RCC即Reset and Clock Control&#xff0c;复位和时钟控制。通过stm32f10x结构图可以看出RCC控制着stm32的AHB系统总线&#xff0c;而AHB总线又桥接APB1和APB2&#xff0c;分别通过它们控制不同的片上外设。如果要使用某个片上外设的功能&#xff0c;必须先通过…

虚拟海外仓用什么系统最好?5个步骤帮你选出适合自己仓库的WMS系统

面对国际市场越来越大的仓储需求&#xff0c;虚拟海外仓的受众还是非常广泛的。不过很多经营虚拟海外仓的企业往往都会陷入管理混乱&#xff0c;低效的怪圈。 要想突破这个经营的瓶颈&#xff0c;快速发展企业&#xff0c;选择一个适合自己的海外仓WMS系统是个不错的选择。 1…

AI音乐神器Suno V3.5进化全解析:功能升级吊炸天,让音乐创作更简单!

前言 目前&#xff0c;suno的v3.5版本已经向Pro和Premier会员开放&#xff0c;从更新当天到现在我已经使用了近2000积分&#xff0c;接下来我将v3.5的使用体验和与旧版本v3进行比较&#xff0c;让大家更直观的感受到v3.5的强大。 其中一个最屌的功能&#xff0c;我放在最后介绍…

Python深度学习基于Tensorflow(12)实战生成式模型

文章目录 Deep Dream风格迁移参考资料 Deep Dream DeepDream 是一项将神经网络学习模式予以可视化展现的实验。与孩子们观察云朵并尝试解释随机形状相类似&#xff0c;DeepDream 会过度解释并增强其在图像中看到的图案。 DeepDream为了说明CNN学习到的各特征的意义&#xff0c…

亚马逊云科技专家分享 | OPENAIGC开发者大赛能量加油站6月5日场预约开启~

由联想拯救者、AIGC开放社区、英特尔联合主办的“AI生成未来第二届拯救者杯OPENAIGC开发者大赛”自上线以来&#xff0c;吸引了广大开发者的热情参与。 为了向技术开发者、业务人员、高校学生、以及个体创业人员等参赛者们提供更充分的帮助与支持&#xff0c;AIGC开放社区特别…

一次编辑00

题目链接 一次编辑 题目描述 注意点 只能进行一次(或者零次)编辑 解答思路 首先判断两个字符串的长度&#xff0c;如果长度相差大于1&#xff0c;说明一次编辑无法通过一次编辑变换而来通过两个指针idx1和idx2指向first和second&#xff0c;初始idx1和idx2指向的都是同一个…

DOM学习

DOM学习 DOM全称是Document Object Model 文档对象模型&#xff0c;就是把文档中的标签&#xff0c;属性&#xff0c;文本&#xff0c;转换为对象来管理 大纲 HTML DOM(文档对象模型)document对象HTML DOM结点 具体案例 HTML DOM(文档对象模型) document对象 使用innerT…

《python本机环境多版本切换》-两种方式以及具体使用--venv/pyenv+pycharm测试

阿丹&#xff1a; source myenv/bin/activate 在开发使用rasa的时候发现自己安装的python环境是3.12的&#xff0c;和rasa不兼容&#xff0c;所以实践一下更换多python环境。 使用虚拟环境 在Python中使用虚拟环境来切换Python版本是一个常见的做法&#xff0c;这可以帮助你…