GC 垃圾回收机制

文章目录

    • JVM 的内存模型
    • 对象存活?
      • 引用计数算法
      • 可达性分析算法
    • 垃圾收集
      • 标记-清除算法
      • 标记-复制算法
      • 标记-整理算法
    • 垃圾收集器
      • 垃圾收集器发展
      • Serial / Serial Old
      • Parallel Scavenge / Parallel Old
      • ParNew / CMS
      • G1
      • ZGC
    • 扩展

JVM 的内存模型

Java 虚拟机(Java Virtual Machine,简称 JVM)根据《Java 虚拟机规范》的规定,Java 虚拟机所管理的内存将会包括以下几个运行时数据区域,如图所示:

对于 Java 应用程序来说,其中 Java 堆(Java Heap)和方法区(元空间或者永久代)是虚拟机所管理的内存中最大的一块。而垃圾回收机制所关注的正是这部分内存该如何管理。

其中方法区垃圾收集的“性价比”通常也是比较低的;在 Java 堆中,尤其是在新生代中,对常规应用进行一次垃圾收集通常可以回收 70%至 99%的内存空间,相比之下,方法区的垃圾收集的回收成果往往远低于此。同时《Java 虚 拟机规范》中提到过可以不要求虚拟机在方法区中实现垃圾收集,事实上也确实有未实现或未能完整实现方法区类型卸载的收集器存在(如 JDK 11 中的 ZGC 收集器就不支持类卸载)。

在 Java 中,垃圾回收机制(Garbage Collection,简称 GC)是一个非常重要的概念,它主要的作用是回收程序当中不再使用的内存。因此 GC 要负责完成 3 项任务:分配内存,确保被引用对象的内存不被错误地回收,回收不再被引用的对象的内存。

对象存活?

在 Java 堆里面存放着几乎所有的对象实例,GC 在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”(“死去”即不可能再被任何途径使用的对象)了,对于“死去”的对象我们会将其标记为垃圾,之后 GC 会将这些进行标记的进行回收。判断对象是否“存活”的算法主要为两种:引用计数算法,可达性分析算法。

引用计数算法

引用计数法通过在堆中对每个对象都有一个引用计数器;当对象被引用时,计数器值就加 1;当引用被置空或者离开作用域时,计数器值就减 1:任何时刻计数器为零的对象就是不可能再被使用的。

引用计数算法虽然简单,但是如果出现两个对象之间互相引用时,两个对象都是垃圾,但是计数器值永远不为 0,GC 就无法对其回收。因此 Java 使用了可达性分析算法来判定对象是否存活的。

可达性分析算法

可达性分析算法就是通过一系列称为“GC Roots’的根对象作为起始节点,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果某个对象不可达时,意味着此对象是不可能再被使用的,就被判定为垃圾。

Java 中固定可作为 GC Roots 的对象包括以下几种:

  • 栈中引用的对象;
  • 类静态属性引用的对象;
  • 常量引用的对象;
  • Native 本地方法中引用的对象;

垃圾收集

首先,基于弱分代假说和强分代假说形成了分代收集理论:

  • 弱分代假说(Weak Generational Hy pothesis):绝大多数对象都是朝生夕灭的。
  • 强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消亡。

根据分代收集理论一般会把 Java 堆划分为新生代(Young Generation)和老年代(Old Generation)两个区域。针对 GC 对回收其中某一个或者某些部分的区域,又划分为“Minor GC"Major GC"和“Full GC 这样的回收类型

  • 新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集。
  • 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。目前只有 CMS 收集器会有单独收集老年代的行为。由于一般 Major GC 发生的时候通常也会伴随着 Minor GC,所以 Major GC 也经常被称为 Full GC 或 FGC,也就是全局范围的 GC 动作。
  • 整堆收集(Full GC):收集整个 Java 堆和方法区的垃圾收集。

标记-清除算法

“标记-清除”(Mark-Sweep)算法分为“标记和“清除两个阶段:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象,统一回收所有未被标记的对象。

当垃圾收集器将内存扫描之后会标记出所有垃圾对象然后将它们回收,但这个方法有一个缺点,就是会不断的会产生大量不连续的内存碎片,使内存的使用率变得越来越低,也可能会导致以后程序无法为大对象分配一片连续的内存空间。

标记-复制算法

标记-复制算法常被简称为复制算法。为了解决标记-清除算法面对大量可回收对象时执行效率低的问题,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当第一块的内存剩余不足时,会将所有需要保留的对象复制到另外一块内存,然后再把已使用过的内存直接清空。

标记-复制算法既做到了垃圾回收,又做到了碎片整理,但代价是将可用内存缩小为了原来的一半,内存的空间浪费一倍。

标记-整理算法

标记-整理(Mak-Compact)算法的标记过程与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。简单来说就是在清理垃圾的基础上,增加了一步碎片整理的操作。

垃圾收集器

垃圾收集器发展

垃圾收集器根据迭代版本分为:

  • 早期的 Serial 和 Serial Old;
  • 中期的 Parallel Scavenge 和 Parallel Old;
  • 过渡期的 ParNew 和 CMS;
  • JDK9 默认垃圾收集器 G1(Garbage First);
  • JDK11 中新加入的低延迟垃圾收集器 ZGC(Z Garbage Collector)。

Serial / Serial Old

Serial 是一个工作在新生代的单线程收集器,与它搭档负责老年代的垃圾收集器为 Serial Old。单线程意味着在进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束,这个动作习惯称为 STW(Stop The World)。Serial/Serial Old 收集器的运行过程如图:

Parallel Scavenge / Parallel Old

Parallel Scavenge 是多线程的收集器,与它搭档的是 Parallel Old。不同于 Serial/Serial Old 收集器的是,当开始垃圾回收时,所有用户线程必须全部暂停,依然触发了 STW,不过这次垃圾回收变成了多线程,对于多 CPU 的服务器来讲,提高了不少效率,不过依然无法避免 STW。

ParNew / CMS

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,与它搭档工作在新生代的垃圾收集器为 ParNew,与 Parallel Scavenge 基本相同。CMS 收集器是基于标记-清除算法实现的,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为四个阶段,括:

  1. 初始标记(CMS initial mark)
  2. 并发标记(CMS concurrent mark)
  3. 重新标记(CMS remark)
  4. 并发清理(CMS concurrent sweep)

其中初始标记、重新标记这两个步骤仍然会触发 STW。初始标记仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快;并发标记阶段就是从 GC Roots 的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行;而重新标记阶段则是为了修正并发标记期间的错标问题,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短;最后是并发清除阶段,清理删除掉标记阶段判断的己经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。CMS 的并发工作流程如下:

G1

G1 是一款主要面向服务端应用的垃圾收集器。JDK9 中,G1 取代 Parallel Scavenge 加 Parallel Old 组合,成为服务端模式下的默认垃圾收集器。G1 允许用户手动设置一个期望的 STW 时间。

G1 基于 Region 的堆内存布局,即 G1 不再坚持固定大小以及固定数量的分代区域划分,而是把连续的 Java 堆划分为多个大小相等的独立区域(Region,Region 的大小可以通过参数-XX:G1Heap RegionSize设定,取值范围为 1MB~32MB,且应为 2 的 N 次幂),每一个 Region 都可以根据需要,扮演新生代的 Ede 空间、Survivor 空间,或者老年代空间,而新生代和老年代的空间大小不再是绝对固定,而且当 GC 扫描内存时,无需扫描整块内存,只需扫描特定区域即可,极大的提高了它所能支持的堆内存的大小

Region 中还有一类特殊的 Humongous 区域,专门用来存储大对象。G1 认为只要大小超过了一个 Region 容量一半的对象即可判定为大对象。每个 Region 的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围为 1MB~32MB,且应为 2 的 N 次幂。而对于那些超过了整个 Region 容量的超级大对象,将会被存放在 N 个连续的 Humongous Region 之中,直至被回收之前,在内存的位置始终保持不变,避免了对老年代整理时频繁的移动大对象。

ZGC

ZGC 是一款在 JDK 11 中新加入的具有实验性质的低延迟垃圾收集器,在 JDK 15 中正式投入生产使用了,使用 –XX:+UseZGC 命令可以启用 ZGC。

ZGC 与 G1 一样,也采用了基于 Region 的堆内存布局,但与不同的是,ZGC 没有分代的概念,而且 Region 具有动态性——动态创建和销毁,以及区域容量大小也是动态的。

在 x64 硬件平台下,ZGC 的 Region 可以具有大、中、小三类容量:

  • 小型 Region(Small Region):容量固定为 2MB,用于放置小于 256KB 的小对象。
  • 中型 Region(Medium Region):容量固定为 32MB,用于放置大于等于 256KB 但小于 4MB 的对象。
  • 大型 Region(Large Region):容量不固定,可以动态变化,但必须为 2MB 的整数倍,用于放置 4MB 或以上的大对象。每个大型 Region 中只会存放一个大对象,这也预示着虽然名字叫作“大型 Region”,但它的实际容量完全有可能小于中型 Region,最小容量可低至 4MB。

ZGC 运作过程如图:

ZGC 做到了几乎整个收集过程都全程可并发,短暂停顿也只与 GC Roots 大小相关而与堆内存大小无关,因而实现了任何堆上停顿都小于十毫秒的目标。

扩展

在命令行中使用以下命令查看 JDK 使用的垃圾收集器:

java -XX:+PrintCommandLineFlags -version

img

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

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

相关文章

转速/线速度/角速度计算FC

工业应用中很多设备控制离不开转速、线速度的计算,这篇博客给大家汇总整理。张力控制的开环闭环方法中也离不开转速和线速度的计算,详细内容请参看下面的文章链接: PLC张力控制(开环闭环算法分析)_plc的收卷张力控制系统_RXXW_Dor的博客-CSDN博客里工业控制张力控制无处不…

按键修改阈值功能、报警功能、空气质量功能实现(STM32)

按键修改阈值功能 要使用按键,首先要定义按键。通过查阅资料,可知按键的引脚如图所示:按键1(S1)通过KEY0与PA0连接,按键2(S2)通过KEY1与PE2连接,按键3(S3&…

收到6家大厂offer,我把问烂了的《Java八股文》打造成3个文档。共1700页!!

前言大家好,最近有不少小伙伴在后台留言,近期的面试越来越难了,要背的八股文越来越多了,考察得越来越细,越来越底层,明摆着就是想让我们徒手造航母嘛!实在是太为难我们这些程序员了。这不&#…

【LINUX】初识文件系统

文章目录一、前言二、回顾C语言文件操作三、初识系统调用openreadwriteclose四、文件系统初识五、结语一、前言 二、回顾C语言文件操作 int main() {FILE* fp fopen("log.txt", "w");if (fp NULL){perror("fopen");}int cnt 0;fputs("…

spring2

1.Spring配置数据源1.1 数据源(连接池)的作用 数据源(连接池)是提高程序性能如出现的事先实例化数据源,初始化部分连接资源使用连接资源时从数据源中获取使用完毕后将连接资源归还给数据源常见的数据源(连接池):DBCP、C3P0、BoneC…

js实现一个简单的扫雷

目录先看下最终的效果:首先来分析一个扫雷游戏具有哪些功能分析完成后我们就开始一步步的实现1. 相关html和css2. 我们使用类来完成相应功能3. 之后我们则是要定义一个地图4. 对地图进行渲染5. 对开始按钮添加点击事件6. 现在我们可以实现鼠标左击扫雷的功能7. 给单…

网络知识汇总

文章目录一、 [网络拓扑结构](https://blog.csdn.net/weixin_52140964/article/details/127786157)二、[空间组网](https://blog.csdn.net/weixin_43509834/article/details/123225995)三、卫星网络发展历程四、SDN的由来五、ECMP、WCMP一、 网络拓扑结构 又称分布式结构。 任…

面试热点题:回溯算法 电话号码的字母组合与组合总和

前言: 如果你一点也不了解什么叫做回溯算法,那么推荐你看看这一篇回溯入门,让你快速了解回溯算法的基本原理及框架 电话号码的字母组合 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。…

2023/3/21总结

题解: E - 2xN Grid (atcoder.jp) 1.这一题,就是求出第一行与第二行相对应的相同数字的个数,看数据有多大,我们就知道,这道题目,是不可能穷举的。 2.因此,我们需要 找到规律去写它。此题需要…

最新消息!信息系统项目管理师教程改版!

《信息系统项目管理师教程(第4版)》于2023年3月出版 新版《信息系统项目管理师考试大纲》于2023年3月出版 信息系统项目管理师教程改版说明 一、整体说明 1、教材整体说明:第四版教材由25章组成,相比第三版28章内容减少了3章主要…

硬件速攻-AT24CXX存储器

AT24C02是什么? AT24CXX是存储芯片,驱动方式为IIC协议 实物图? 引脚介绍? A0 地址设置角 可连接高电平或低电平 A1 地址设置角 可连接高电平或低电平 A2 地址设置角 可连接高电平或低电平 1010是设备前四位固定地址 &#xf…

QT | 编写一个简单的上位机

QT | 编写一个简单的上位机 时间:2023-03-19 参考: 1.易懂 | 手把手教你编写你的第一个上位机 2.QT中修改窗口的标题和图标 3.图标下载 1.打开QT Creator 2.新建工程 Qt Creator 可以创建多种项目,在最左侧的列表框中单击“Application”&am…

学校教的Python,找工作没企业要,太崩溃了【大四真实求职经历】

如果只靠学校学的东西去找工作,能找到工作吗? 今天给大家看一个粉丝的真实求职案例,想做Python方面的工作,投了二十几个简历却没人要,心态崩了。为什么没人要?我来告诉你答案。 然后我还会结合我的这些年的…

Linux--线程安全、多线程fork问题

目录 一、概念: 二、利用空格分割字符串: 1.代码: 2.结果: 3.解决方法: 三、多线程fork() 1.代码: 2.线程id 3.增加fork()代码: 4.改变fo…

【嵌入式硬件芯片开发笔记】HART协议调制解调芯片AD5700配置流程

【嵌入式硬件芯片开发笔记】HART协议调制解调芯片AD5700配置流程 XTAL_EN接地,CLK_CFG的两个引脚由同一个GPIO控制 初始时HART_CLK_CFG输出低电平 由RTS引脚控制调制/解调。当RTS处于高电平时,为解调(输入);否则为调…

【计算机组成原理】:计算机系统概述

目录 一、计算机系统层次结构 1️⃣计算机系统的组成 2️⃣计算机硬件 1. 冯诺依曼机的基本思想 💤思考:冯诺依曼机的来源❓ 🌸知识点:冯诺依曼机的特点 💤思考:以运算器为中心的计算机有什么缺点❓ 2…

【微服务】—— 统一网关Gateway

文章目录1. 概述1.1 为什么需要网关1.2 SpringCloud Gateway2. gateway快速入门搭建网关服务1、创建新的module,引入SpringCloudGateway的依赖和nacos的服务发现依赖:2、编写路由配置和nacos地址3. 断言工厂路由断言工厂Route Predicate Factory4. 过滤器…

【数据结构】千字深入浅出讲解队列(附原码 | 超详解)

🚀write in front🚀 📝个人主页:认真写博客的夏目浅石. 🎁欢迎各位→点赞👍 收藏⭐️ 留言📝 📣系列专栏:C语言实现数据结构 💬总结:希望你看完…

javaweb实习实训管理系统mysql

本毕业设计基于JSP的实习实训管理系统,本系统能实现网上的实习实训信息管理,主要功能有:添加用户、查看用户、管理用户、添加实验室、查看实验室、管理实验室、添加课程、查看课程、管理课程、添加教学、查看教学、管理教学、添加实习、查看实…

STM32的推挽输出和开漏输出

文章目录 前言一、推挽输出二、开漏输出三、区别和适应场景总结前言 本篇文章将带大家了解STM32的推挽输出和开漏输出,并且学习这两个的区别,学习分别在什么时候使用这两个不同的输出方式。 在 STM32 微控制器中,GPIO(General Purpose Input/Output)模块是一个通用的输入…