【JVM】垃圾回收机制(Garbage Collection)

目录

一、什么是垃圾回收?

二、为什么要有垃圾回收机制(GC)?

三、垃圾回收主要回收的内存区域

四、死亡对象的判断算法

a)引用计数算法

b)可达性分析算法

五、垃圾回收算法

a)标记-清除算法

b)复制算法

c)标记-整理算法

d)分代算法

六、垃圾回收器


一、什么是垃圾回收?

垃圾回收指的是自动管理和释放不再被程序使用的内存资源的过程。在程序运行过程中,动态分配的内存会被使用,但也需要在不再需要时释放,以免占用过多的内存空间,导致内存泄漏或内存溢出等问题。

二、为什么要有垃圾回收机制(GC)?

早在学习C语言中,有这么一块内容:动态内存管理。动态内存管理的主要内容就是通过 malloc 申请内存,free 释放内存。

  • 此处 malloc 申请到的内存,生命周期是跟随整个进程的。
  • 这一点对于服务器程序就很不友好了,服务器每个请求都去 malloc 一块内存,如果没有 free 释放,就会使申请的内存越来越多,后续想要申请内存就无法申请了。这就是内存泄漏问题。

而在实际开发中,很容易出现 free 不小心就忘记调用了,或者因为一些情况导致 free 没有被执行到,例如代码块中存在 if 导致的 return 或者 抛出异常了(当前C语言没有异常这一说)。因此,内存泄漏是一个很大的问题。

能否让释放内存的操作,由程序自动负责完成,而不是依赖程序员手动释放呢?

  • 此时就引入了垃圾回收机制(GC),来解决上述问题。

三、垃圾回收主要回收的内存区域

JVM 在运行 Java 程序时,将内存划分为不同的区域,称为运行时数据区。这些区域包括方法区、堆、栈(虚拟机栈)、本地方法栈和程序计数器等。程序在执行之前先要把 Java 代码转换成字节码(.class文件),JVM 首先需要把字节码通过一定的方式(类加载器)把文件加载到内存中(即运行时数据区)。

对于程序计数器、虚拟机栈、本地方法栈这三部分区域而言,其生命周期与相关线程有关,随线程而生,随线程而灭。并且这三个区域的内存分配与回收具有确定性,因为当方法结束或者线程结束时,内存就自然跟着线程回收了。

而对于方法区,方法区主要存储的是类加载后的元数据信息(静态变量,常量池等),这些信息在程序运行期间基本上是不会发生变化的。

因此,在Java虚拟机中,垃圾回收主要针对的是 堆 区域进行回收。

前面说到,垃圾回收指的是自动管理和释放不再被程序使用的内存资源的过程,在Java堆中,存放着几乎所有的对象实例,因此内存回收,也可以叫做死亡对象的回收。

垃圾回收器在对堆进行垃圾回收前,首先要判断这些对象哪些还存活,哪些已经“死去”。判断对象是否已“死”有以下几种算法。

四、死亡对象的判断算法

a)引用计数算法

引用计数法的描述如下: 

  • 引用计数算法是一种简单的垃圾回收算法,它基于对象的引用计数来确定何时可以回收对象。在引用计数算法中,每个对象都会有一个与之关联的引用计数,用来记录有多少个指针指向该对象。当引用计数为0时,表示该对象不再被引用,可以安全地回收。
  • 这种算法的实现相对简单,通常在对象创建和销毁时维护引用计数。当有新的指针指向对象时,引用计数加1;当指针不再指向对象时,引用计数减1。当引用计数减到0时,垃圾回收器会立即回收该对象。

引用计数算法的优点是实现简单,且判定效率也比较高,可以及时回收不再使用的对象。比如Python语言就采用引用计数法进行管理内存。

但是,在主流的JVM中没有选用引用计数法来管理内存,最主要的原因就是引用计数法无法解决对象的循环引用问题。即如果两个或多个对象之间存在循环引用,它们的引用计数永远不会为0,即使它们已经不再被程序使用,也无法被回收。这会导致内存泄漏,占用大量的内存空间。

b)可达性分析算法

JVM 就是采用“可达性分析”来判断对象是否存活(同样采用此法的还有C#、Lisp),该算法可以有效处理循环引用问题,其核心思想为:

  • 通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索(遍历),搜索走过的路径称之为“引用链”,当一个对象到GC Roots没有任何的引用链相连时(从GC Roots到这个对象不可达),证明此对象是不可用的。
  • 一旦完成了可达性分析,JVM 就可以安全地回收那些不可达的对象,释放它们所占用的内存空间。

如上图所示,对象Object5-Object7之间虽然彼此还有关联,但是它们到GC Roots是不可达的,因此它们会被判定为可回收对象。

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

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象;
  2. 方法区中类静态属性引用的对象;
  3. 方法区中常量引用的对象;
  4. 本地方法栈中 JNI(Native方法)引用的对象。

通过可达性分析算法,JVM 就能够将死亡对象标记出来了,标记出来之后就要进行垃圾回收操作。在介绍垃圾回收器之前,先介绍垃圾回收器使用的几种算法。

五、垃圾回收算法

a)标记-清除算法

“标记-清除”算法是最基础的垃圾回收算法。算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。

后续的回收算法都是基于这种思路,并对其不足加以改进而已。

“标记-清除”算法的不足主要有两个:

  1. 效率问题:标记和清除这两个过程的效率都不高,特别是在堆中对象数量庞大的情况下。
  2. 空间问题:标记清除后会产生大量不连续的内存碎片,内存碎片太多可能会导致以后在程序运行中,需要分配较大对象时,无法找到足够连续的内存而导致内存分配失败,或不得不提前触发另一次垃圾回收。

b)复制算法

“复制”算法解决了“标记-清除”算法两个主要问题,即内存碎片化问题和效率问题。

  • 它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。
  • 当这块内存需要进行垃圾回收时,会将此区域还存活着的对象复制到另一块上面,然后再把已经使用过的内存区域一次清理掉。

这样做的好处就在于不仅解决了“标记-清除”的主要问题,且算法实现简单,运行高效。解决内存碎片化问题主要体现在,复制过程中通过移动堆顶指针,按顺序进行分配,使碎片化内存被复制到连续的一块空间。

不过,复制算法也有一些缺点:例如在一轮GC中,大部分对象都是长期存活的,只有少数对象需要回收,那么复制算法就会造成大量的复制操作,导致影响性能。

c)标记-整理算法

“标记-整理”算法的提出,则是为了解决“复制”算法在对象存活率较高时,会进行比较多的复制操作这样的问题。

  • 标记过程仍与“标记-清除”算法的标记过程一致;
  • 但后续步骤不是直接对可回收对象进行回收,而是让所有存活对象都向一端移动,以便整理出一块连续的内存空间。这个思想类似于双指针算法,但其移动对象的过程更复杂。

上述过程仍有一些缺点:即移动存活对象的过程会带来较大的开销。

d)分代算法

综合以上三种算法,JVM并没有直接使用其中的一种,而是结合这三种算法,搞出了一个“综合性”方案,即分代算法。

实际上,堆里面分为两个区域:新生代和老生代,新生代放新建的对象,当经过一定 GC 次数之后还存活的对象才放入老生代。新生代还有三个区域:一个 Endn + 两个 Survivor(S0/S1).

分代算法就是通过区域划分,实现不同区域使用不同的的垃圾回收策略,从而实现更好的垃圾回收。

这就好比中国的一国两制方针一样,对于不同的情况和地域设置更符合当地的规则,从而实现更好的管理,这就是分代算法的设计思想。 

当前 JVM 垃圾回收都采用的是“分代”算法。在新生代中,每次垃圾回收都有大批的对象死去,只有少量存活,因此采用复制算法;而老年代中对象存活率高、没有额外空间对它进行分配担保,就必须采用“标记-清除”或者“标记-整理”算法。

  • 对于新生代来说,98%的对象都是“朝生夕死”的,所以并不需要按照1:1的比例来划分内存空间,而是将新生代内存分为一块比较大的Eden(伊甸园)空间和两块比较小的Survivor(幸存者)空间,每次使用Eden和其中一块Survivor(两个Survivor区域,一个称为From区,一个称为To区)。
  • 当回收时,将Eden和Survivor中还存活的对象一次性复制到另一块Survivor上,最后清理掉Eden和刚才使用过的Survivor空间。完成一次对象复制后,From区和To区的角色会互换。
  • HotSpot虚拟机默认Eden与Survivor的大小比例是8:1,也就是说Eden:Survivor From:Survivor To = 8:1:1。所以每次新生代可用内存空间为整个新生代容量的90%,而剩下的10%用来存放回收后存活的对象。

哪些对象会进入新生代?哪些对象会进入老年代?

  • 新生代:一般新创建的对象都会进入新生代;
  • 老年代:大对象和经历了 N 次(一般情况默认是 15 次)垃圾回收依然存活下来的对象会从新生代移动到老年代。

面试题:请问了解 Minor GC 和 Full GC 吗?这两种GC有什么不一样吗?

  • Minor GC又称为新生代GC,指的是发生在新生代的垃圾回收。因为新生代中的Java对象大多都具有朝生夕灭的特性,因此Minor GC(采用复制算法)非常频繁,一般回收速度也比较快。
  • Full GC又称为老年代GC或者Major GC:指发生在老年代的垃圾回收。出现了Major GC,经常会伴随至少一次的Minor GC(并非绝对)。Major GC的触发频率通常比较低,且速度一般会比Minor GC慢10倍以上。

六、垃圾回收器

如果说上面所讲的垃圾回收算法是内存回收的方法论,那么垃圾回收器就是内存回收的具体实现。

垃圾回收器的作用:垃圾回收器是为了保证程序能够正常、持久运行的一种技术,它是将程序中的死亡对象,也就是垃圾对象进行清除,从而保证了新对象能够正常申请到内存空间。

以下这些回收器是 HotSpot 虚拟机随着不同版本推出的重要的垃圾回收器:

上图展示了7种作用于不同分代的垃圾回收器,如果两个回收器之间存在连线,就说明它们之间可以搭配使用。所处的区域,表示它是属于新生代回收器还是老年代回收器。

为什么会有这么多垃圾回收器?

  • 其主要原因是:自从有了 Java 语言就有了垃圾回收器,这么多垃圾回收器其实是历史发展的产物。随着时间的推移和技术的进步,人们对垃圾回收器的性能、吞吐量、停顿时间等方面提出了更高的要求,因此就产生了更多更新的垃圾回收器。

此处就不展开介绍每种垃圾回收器了。

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

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

相关文章

GDPU 天码行空11

(一)实验目的 1、掌握JAVA中IO中各种类及其构造方法; 2、重点掌握IO中类所具有的IO操作方法; 3、熟悉软件中登录模块的开发方法; 4、掌握IO中读写常用方法。 5、进一步熟悉正则规则的使用方法。 (二&…

ue引擎游戏开发笔记(32)——为游戏添加新武器装备

1.需求分析: 游戏中角色不会只有一种武器,不同武器需要不同模型,甚至可能需要角色持握武器的不同位置,因此需要添加专门的武器类,方便武器后续更新,建立一个武器类。 2.操作实现: 1.在ue5中新建…

如何用DeepDiff测接口数据源变更?

开发同学最近变更了部分业务查询接口底层的数据源,希望测试同学能够针对这些接口进行一些回归验证,校验底层数据源更新前后业务查询接口返回的一致性,保证更新后对正常业务没有影响。 这个回归测试和一般接口测试有所区别,不仅仅…

使用Simulink Test进行单元测试

本文摘要:主要介绍如何利用Simulink Test工具箱,对模型进行单元测试。内容包括,如何创建Test Harness模型,如何自动生成excel格式的测试用例模板来创建测试用例,如何手动填写excel格式的测试用例模板来手动创建测试用例…

Redis---------实现查询缓存业务

目录 数据库与缓存之间的工作业务逻辑: 接下来看查询缓存代码实现,主要是捋清楚业务逻辑,代码实现是死的: Controller: Service: P37作业实现:总体逻辑跟上面的业务逻辑差不多 Controller: Service&#…

Azure AKS日志查询KQL表达式

背景需求 Azure(Global) AKS集群中,需要查询部署服务的历史日志,例如:我部署了服务A,但服务A的上一个版本Pod已经被杀掉由于版本的更新迭代,而我在命令行中只能看到当前版本的pod日志&#xff…

SparkSql介绍

概述 SparkSQL,顾名思义,就是Spark生态体系中的构建在SparkCore基础之上的一个基于SQL的计算模块。SparkSQL的前身不叫SparkSQL,而叫Shark,最开始的时候底层代码优化,sql的解析、执行引擎等等完全基于Hive&#xff0c…

科创板门槛升级!解析中国量子企业的上市之路与国际比拼

4月30日晚,中国证监会于发布了修订后的《科创属性评价指引(试行)》(以下简称“新指引”),该指引自发布日起正式生效。本次修订对原有指引中的部分标准进行了调整,具体如下: 1&#x…

如何用Kimi,5秒1步生成流程图

引言 在当前快节奏的工作环境中,拥有快速、专业且高效的工具不可或缺。 Kimi不仅能在5秒内生成专业的流程图(kimi),还允许实时编辑和预览,大幅简化了传统流程图的制作过程。 这种迅速的生成能力和高度的可定制性使得…

Day28:ElasticSearch入门、Spring整合ES、开发社区搜索功能

ElasticSearch入门 Elasticsearch简介 一个分布式的、Restful风格的搜索引擎。支持对各种类型的数据的检索(非结构化的也可以)。搜索速度快,可以提供实时的搜索服务。便于水平扩展(集群式部署),每秒可以处…

一文读懂计算机视觉4大任务:分类任务、检测任务、目标分割任务、关键点检测任务

《博主简介》 小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~ 👍感谢小伙伴们点赞、关注! 《------往期经典推…

算法系列--多源BFS问题

💕"对相爱的人来说,对方的心意,才是最好的房子。"💕 作者:Lvzi 文章主要内容:算法系列–多源BFS问题 大家好,今天为大家带来的是算法系列--多源BFS问题 前言: 之前我们已经学习过单源的最短路问…

【Toritoise SVN】SVN 怎么忽略文件夹下的所有文件但是不忽略文件夹本身

比如:忽略 Assets\StreamingAssets\LocalAsset文件夹下的所有文件但是不忽略LocalAsset这个文件夹 在TortoiseSVN中,你可以通过以下步骤来修改文件夹的svn:ignore属性: 打开Windows资源管理器,导航到你的工作副本中的Assets\Stre…

VMware虚拟机问题解决方案

1、运行虚拟机系统蓝屏 可能的原因有两个: 1). 虚拟机所在磁盘的空间不足 ; -------> 清理磁盘空间 。 2). 操作系统版本高, 需要适配新版本的Vmware ; ------> 卸载Vmware15版本, 安装Vmware16版本 。 2、卸载VMware的步骤 1)卸载已经安装的VMware 从控制面…

8G防火墙,WAF防火墙,可抵御各种恶意请求、恶意机器人、攻击防御

8G防火墙,WAF防火墙,可抵御各种恶意请求、恶意机器人、攻击防御 经过一年多的 beta 测试,8G 防火墙已准备好在生产现场使用。因此,您可以受益于 nG 防火墙(又名 nG 黑名单)的最新发展提供的强大保护。8G 防…

源代码防泄露可以通过哪些方法实现?七种有效方法分享

在当今数字化时代,访问安全和数据安全成为企业面临的重要挑战。传统的边界防御已经无法满足日益复杂的内网办公环境,层出不穷的攻击手段已经让市场单一的防御手段黔驴技穷。当企业面临越来越复杂的网络威胁和数据泄密风险时,更需要一种综合的…

iOS审核被拒 无法添加以供审核 要开始审核流程,必须提供以下项目

iOS审核被拒 最近有小伙伴反馈上架被拒,提示如下: 无法添加以供审核 要开始审核流程,必须提供以下项目 提交以供审核时遇到意外错误。如果问题仍然存在,请联系我们 后来小伙伴解决方案是:把备案号大写改为小写。 竟…

Ansible自动化工具模块调用与playbook编写

目录 一、Ansible工作机制与特点 (一)Ansible工作机制 1. 初始化与配置 2. 编写Playbook 3. 调用模块 4. 加密敏感数据 5. 执行Playbook 6. 收集执行结果 7. 错误处理与回滚 8. 反馈与报告 (二)Ansible 的主要特点包括…

Java并发编程: Synchronized锁升级

文章目录 一、jdk8 markword实现表二、使用工具来查看锁升级 一、jdk8 markword实现表 new -> 偏向锁 -> 轻量级锁(自旋锁、自适应自旋锁)-> 重量级锁: 偏向锁和轻量级锁都是用户空间完成的。重量级锁是需要向内核申请的。 synchr…

深度学习之基于Matlab神经网络的活体人脸和视频人脸识别系统

欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 人脸识别技术作为生物识别技术的一种,近年来得到了广泛的关注和应用。与传统的身份认证方…