【JVM】垃圾回收机制、算法和垃圾回收器

news2024/9/22 5:35:01

什么是垃圾回收机制

为了让程序员更加专注于代码的实现,而不用过多的考虑内存释放的问题,所以在Java语言中,有了自动的垃圾回收机制,也是我们常常提及的GC(Garbage Collection)

有了这个垃圾回收机制之后,程序员只需要考虑内存的申请即可,内存的释放由系统自动识别完成。在垃圾回收的时候,不同的对象引用类型,GC会采用不同的回收时机,换句话说自动垃圾回收算法就会变得非常重要,如果因为算法的不合理,导致内存资源一直没有释放,同样也可能导致内存资源一直没有被释放,同样也可能会导致内存溢出

对象什么时候可以被垃圾回收

简单用一句话来说:人如果一个或者多个对象没有任何的引用指向它,那么这个对象现在就是垃圾,如果定位成垃圾,那么就有可能被垃圾回收器回收

如果要定位什么是垃圾,有两种方式来确定,第一个是引用计数法,第二个是可达性分析算法

引用计数法

一个对象被引用一次,在当前对象头上递增一次引用次数 ,如果这个对象的引用次数为0,代表这个对象可以被回收

String demo = new String("123");

String demo = null;

 

当对象间出现了循环引用的话,则引用计数法就会失效,如下图:

 

虽然a和b都为null,但是由于a和b存在循环引用,这样a和b永远都不会被回收

引用计数法的优点:

  • 实时性较高,无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否为0,就可以直接回收
  • 在垃圾回收过程中,应用无需挂起。如果申请内存时,内存不足,则立刻报OOM错误
  • 区域性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象

缺点:

  • 每次对象被引用时,都需要去更新计数器,有一点时间开销
  • 浪费 CPU 资源,即使内存够用,仍然在运行时进行计数器的统计
  • 无法解决循环引用问题,会引发内存泄露 (最大的缺点)

可达性分析算法

现在的虚拟机采用的都是通过可达性分析算法来确定哪些内容是垃圾

首先会存在一个根节点(GC Roots),引出它下面指向的下一个节点,在以下一个节点开始为开始找到它下面的节点,依次往下类推,直到所有节点全部遍历完毕。这个思想类似于算法中的并查集

如上图,X和Y就是GC Roots无法到达的节点,那么X和Y就可以被认为是垃圾

根对象是那些肯定不能当作垃圾回收的对象,就可以当作根对象。一般有四种类型可以选做根对象:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(即一般说的Native方法)引用的对象(不常用)

JVM垃圾回收算法有哪些

刚刚已经讲解完了如何定义垃圾,那么定义完成之后的垃圾有一系列的处理算法

标记清除算法

标记清除算法,是将垃圾回收分为两个阶段,分别是标记清除

  • 根据可达性分析算法得出了垃圾进行标记
  • 对这些标记为可回收的内容进行垃圾回收

标记清除算法有一定的劣势:

  • 效率比较低,标记和清除两个动作都是需要遍历所有的对象,并且GC的时候,需要停止应用程序,对于交互性要求比较高的应用而言这个不是很推荐
  • (重要)通过标记清除算法清理出来的内存,碎片化比较严重,因为被回收的对象可能存在于内存的各个角落,所以清理出来的内存不是很连贯

复制算法

复制算法的核心就是,将原来的内存空间一分为二每次都是用其中的一块内存。在垃圾回收的时候,将正在使用的对象复制到另一个内存空间中,然后将该内存空间清空交换两个内存的角色,完成垃圾的

如果内存中的垃圾对象较多,需要复制的对象就较少,这种情况下适合使用该 方式并且效率比较高,反之,则不适合

复制算法的执行流程: 

  • 将内存区域分成两个部分,每次只是操作其中的一个
  • 当进行垃圾回收的时候,将正在使用的内存区域中的存活对象移动到未使用的内存区域。当移动完成之后,对这一部分内存进行一次性清除

优点:

  • 在垃圾对象多的情况下,效率较高
  • 清理后,内存无碎片

缺点:

  • 分配的2块内存空间,在同一个时刻,只能使用一半,内存使用率较低

标记整理算法

标记整理算法是在标记清除算法的基础上,做了优化和改进的算法。和标记清除算法一样,也是从根节点开始,对对象的引用进行标记,在清理阶段,并不是简单的直接清理可回收对象,而是将存活对象都向内存的另一端移动,让然后清理边界以外的垃圾,从而解决了碎片化的问题

标记整理算法的执行流程:

  • 标记垃圾
  • 需要清楚的向右走,不需要清楚的向左走
  • 清除边界以外的垃圾

优缺点:

  • 解决了标记清除算法的碎片化的问题
  • 标记压缩算法多了一步,对象移动内存位置的步骤,其效率也有有一定的影响
  • 复制算法标记完就复制,但标记整理算法得等把所有存活对象 都标记完毕,再进行整理

分代收集算法

在Java8时,堆被分成两份新生代老年代(1:2),在Java7的时候,还存在一个永久代,Java8将其移动到本地内存的元空间中

对于新生代,内部被分为三个区域:Eden区,两个大小完全相同的survivor区S0(from)和S1(to)(8:1:1)

分代收集算法执行流程:

  • 新建的对象都会先分配代eden区,当eden区内存不足的时候,会标记eden区和from区(现阶段还没有)的存活对象
  • 将存活下来的对象采用复制算法复制到to中,复制完毕之后,eden和from内存得以释放
  •  经过一段时间之后,eden区的内存又不足了,标记eden区和to区存活的对象,将存货的对象复制到from区
  •  当幸存区对象熬过几次回收(最多15次),晋升到老年代(幸存区内存不足 或大对象会导致提前晋升)

MinorGC 、 Mixed GC 、 FullGC 的区别是什么 

  • MinorGC【young GC】发生在新生代的垃圾回收,暂停时间短(STW:暂停所有应用程序线程,等待垃圾回收的完成)
  • Mixed GC 新生代 + 老年代部分区域的垃圾回收,G1 收集器特有
  • FullGC 新生代 + 老年代完整垃圾回收,暂停时间长(STW),应尽力避免

JVM垃圾回收器有哪些

在JVM中,实现了多种垃圾收集器,包括:

  • 串行垃圾收集器
  • 并行垃圾收集器
  • CMS(并发)垃圾收集器
  • G1垃圾收集器

串行垃圾收集器

Serial和Serial Old串行垃圾收集器,是指使用单线程进行垃圾回收,堆内存较小,适合个人电脑

  • Serial作用于新生代,曹勇复制算法
  • Serial Old作用于年轻代,采用标记-整理算法

垃圾回收时,只有一个线程在工作,并且Java应用中所有线程都要暂停(STW),等待垃圾回收的完成

并行垃圾收集器

Parallel New和Parallel Old是一个并行垃圾回收器,JDK8默认使用此垃圾回收器

  • Parallel New作用于新生代,采用复制算法
  • Parallel Old作用于老年代,采用标记-整理算法

垃圾回收时,多个线程在工作,并且java应用中的所有线程都要暂停(STW), 等待垃圾回收的完成

 CMS (并发)垃圾收集器

CMS全称 Concurrent Mark Sweep,是一款并发的、使用标记-清除算法的垃圾回收器,该回收器是针对老年代垃圾回收的,是一款以获取最短回收停顿时间为目标的收集器,停顿时间短,用户体验就好。其最大特点是在进行垃圾回收时,应用仍然能正常运行

G1垃圾收集器

应用于新生代和老年代,在JDK9之后默认人使用的G1垃圾收集器。其中划分了很多个区域,每个区域都可以充当eden,survivor,old,humongous,其中humongous专门为大对象准备的。采用的复制算法,并且注重于响应时间吞吐量。运行时主要是分为三个阶段:新生代回收并发标记混合收集。如果出现并发失败(即回收速度赶不上创建新对象的速度),就会触发Full GC

下面来详细的讲解一下年轻代垃圾回收

  • 初始的时候,所有的区域都处于空闲状态
  • 创建了一些对象,挑出一些空闲区域作为eden区存储这些对象
  • 当eden区需要垃圾回收时,挑出一个空闲区域作为survivor,用复制算法复制存活对象,需要暂停用户线程
  •  随着时间流逝,eden区的内存又有不足,将eden区以及之前幸存区中存活的对象,采用复制算法,复制到新的幸存区,其中比较老的对象晋升至老年代

下面来说一下下一个阶段年轻代垃圾回收 + 并发标记:

  • 当老年代占用内存超过一定的阈值(默认是45%)后,触发并发标记,这个时候无需暂停用户线程
  • 并发标记之后,会有重新标记阶段解决漏标问题,此时需要暂停用户线程 
  • 这些都完成后就知道了老年代有哪些存活对象,随后进入混合收集阶段。此时不会对所有老年代区域进行回收,而是根据暂停时间目标优先回收价值高 (存活对象少)的区域(这也是 Gabage First 名称的由来)

混合垃圾回收的执行流程:

复制完成,内存得到释放。进入到下一轮的新生代回收、并发标记、混合收集

其中H叫做大对象,如果对象非常大,就会开辟一块连续的空间存储巨型对象

强引用、软引用、弱引用、虚引用的区别

强引用:只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能 被垃圾回收

User user = new User()

 软引用:仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收

User user = new User();
SoftReference softReference = new SoftReference(user);

 

弱引用:仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象

User user = new User();
WeakReference weakReference = new WeakReference(user);

虚引用:必须配合引用队列使用,被引用对象回收时,会将虚引用入队,由Reference Handler线程调用虚引用相关方法释放直接内存

User user = new User();
ReferenceQueue referenceQueue = new ReferenceQueue();
PhantomReference phantomReference = new PhantomReference(user,queue);

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

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

相关文章

华为路由常见 LSA 类型的产生及作用域和字段详细解读

华为路由常见 LSA 类型的产生及作用域 类型名称描述1路由器 LSA(Router LSA)每个设备都会产生,描述了设备的链路状态和开销。该 LSA 只能在接口所属的区域内泛洪2网络 LSA(Network LSA)由 DR 产生,描述该 …

在cPanelWHM中如何重置 MySQL 用户帐户密码

更改MySQL用户账户密码非常简单。服务器管理员可以在WHM中编辑任何MySQL用户的帐户。cPanel用户可以编辑其帐户管理的数据库的密码。 在WHM中更改MySQL用户帐户密码 打开WHM,在侧边菜单中的SQL服务下选择“Change MySQLUser Password”。Hostease的服务器产品提供稳…

NeRF学习——复现训练中的问题记录

代码复现的框架是基于:pengsida 的 Learning NeRF 希望各位可以通过学习 NeRF-Pytorch 的源码来自己复现一下试试看! 文章目录 1 Windows bug1.1 DataLoader 的多进程 pickle1.2 imageio 输出图片1.3 I/O 2 训练问题2.1 Evaluate 显存爆炸2.2 尝试一2.…

基于VScode和C++实现Protobuf数据格式的通信

目录 1. Protobuf 概述1.1 定义1.2Protobuf的优势 2. Protobuf 语法3、序列号和反序列化3.1 .pb.h 头文件3.2 序列化3.3 反序列化 4、测试用例 Protobuf详细讲解链接 1. Protobuf 概述 1.1 定义 protobuf也叫protocol buffer是google 的一种数据交换的格式,它独立…

递归题解集

目录 递归: 一、汉诺塔问题 1.题目链接:面试题 08.06. 汉诺塔问题 2.题目描述: 3.解法:(递归) 🌵算法思路: 🌵算法流程: 🌵算法代码&…

掌握SEO站外推广优化的五大绝招

对于网站运营者和数字营销人员来说,SEO站外推广是提升网站流量和排名的重要手段。以下是五个有效的SEO站外推广优化方法,希望对大家有所帮助。 1. 高质量的外链建设 高质量的外部链接(Backlinks)是搜索引擎排名的重要因素之一。…

Openboxes开发环境配置及本地化、API测试

目录 Openboxes简介 开发环境配置及启动 更新中文多语言配置 API测试 Openboxes简介 Openboxes是一款开源的仓库管理软件,提供了库存管理、采购管理、销售管理等功能,可以帮助用户高效地管理仓库及库存。并提供了丰富的API接口。系统基于java8 和Gr…

关于微信,qq小程序的登录,数据库-前端-接口解析

一、背景 当我们在对接微信平台,开发微信小程序时,用户标识是必不可少的。用户标识贯通了整个开发过程,所以获取到唯一的用户标识是必须的。 二、多平台兼容 因为考虑到一次开发,可多端运行,需要考虑兼容多平台兼…

SAP 财务管理系统 —— 企业财务智能化的领航者

在当今数字化时代,企业财务管理的智能化已成为推动企业持续增长的关键因素。SAP 财务管理系统通过智能化技术,帮助财务部门提高收入、控制成本并降低财务风险,释放财务数字化转型的价值。财务 ERP 作为 SAP 的核心组成部分,将帮助…

【机器学习】成本函数在逻辑回归中的形式以及逻辑回归的成本函数

引言 在机器学习中,逻辑回归是一种用于二分类问题的线性回归的扩展。逻辑回归的成本函数是用来评估模型预测结果与实际结果之间差异的函数 文章目录 引言一、成本函数在逻辑回归中的形式1.1 成本函数的具体形式1.1.1 对于单个训练样本 二、逻辑回归的成本函数实现2.…

股指期货的期现套利及其作用是什么?

股指期货市场提供了一种独特的投资机会,即通过期现套利策略来捕捉无风险利润。这种策略基于股指期货与对应的现货指数之间的价格差异。当这种差异超出正常范围时,投资者可以利用套利操作来实现稳定收益。 期现套利的核心在于同时买入现货指数的代表性投…

STM32DMA数据传输

我估计大多数人学这么久连听说都没听说过DMA,更不用提知道它是干嘛的。其实DMA的本质就是一个数据的搬运工。平常的时候当我们没有配置的时候,一直都是CPU在搬运数据,但是这个活又累又没有技术含量,所以DMA的重要性还是有的。 目…

centos8.5.2111切换阿里云镜像

备份 mv /etc/yum.repos.d/CentOS-Linux-BaseOS.repo /etc/yum.repos.d/CentOS-Linux-BaseOS.repo.backup下载最新的源 wget -O /etc/yum.repos.d/CentOS-Linux-BaseOS.repo http://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo更改第二个配置文件 cd /etc/yum.repo…

自定义CustomRatingBar控件

通过自定义RatingBar的样式实现⭐️⭐️⭐️指示器的方式功能过于受限,而且显示的样式阴影会受到影响。 系统自带显示: 自定义样式: 因此简单自一个符合要求的 CustomRatingBar 支持设置星星数量支持设置星星Rating(float)支持设置空显示…

计算机毕业设计选题推荐-健康饮食系统-Java/Python项目实战

✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

【优秀python 数据分析案例】基于python的穷游网酒店数据采集与可视化分析的设计与实现

1 需求分析 1.1 用户需求 1.1.1 背景与现状 穷游网是国内知名的旅游社区,在其网站上,用户可以自由分享旅行经验和攻略,也可以浏览其他用户的经验和攻略,以便更好地规划自己的旅行。而酒店信息是旅行攻略中不可或缺的一部分&#…

正点原子imx6ull-mini-Linux驱动之设备树下的 platform 驱动编写(15)

上一章我们详细的讲解了 Linux 下的驱动分离与分层,以及总线、设备和驱动这样的驱动 框架。基于总线、设备和驱动这样的驱动框架,Linux 内核提出来 platform 这个虚拟总线,相应 的也有 platform 设备和 platform 驱动。上一章我们讲解了传统的…

vue2学习 -- 脚手架

文章目录 1. 使用2. render函数3. vue.config.js配置文件4. ref属性5. props配置6. mixin7. 插件8. scoped 1. 使用 脚手架文档 安装脚手架 npm install -g vue/cli 选择工作目录,创建项目 vue create name 2. render函数 关于不同版本的Vue.js vue.js与vue.run…

在 VueJS 项目中实现多个可拖拽的弹出框(多个可拖拽el-dialog弹出框,共用同一函数)

前言 在项目开发中,弹出框(Dialog)是常见的UI组件。默认情况下,弹出框的位置是固定的,但在某些场景下,我们希望用户可以自由拖动弹出框的位置,以提升用户体验。之前单个视频拖拽弹框&#xff0…

Spring Boot 参数校验 Validation 使用

概述 当我们想提供可靠的 API 接口,对参数的校验,以保证最终数据入库的正确性,是必不可少的活。前、后端校验都是保证参数的准确性的手段之一,前端校验并不安全,任何人都可以通过接口来调用我们的服务,就算…