垃圾收集篇

news2024/11/23 11:35:18

文章目录

  • 垃圾收集算法
    • 垃圾的概念
    • 对象存活的判断
      • 引用计数器法
      • 可达性分析算法
  • 算法
    • 标记清除算法
    • 复制算法
    • 标记压缩算法
  • 垃圾收集的相关概念
    • STW
    • 安全点
    • 安全区域
  • 垃圾收集器
    • 重要指标
      • 吞吐量
      • 停顿时间
    • 垃圾收集器的分类
    • Serial 收集器:串行回收
    • ParNew 收集器:并行回收
    • Parallel Scavenge 收集器:吞吐量优先
    • CMS收集器:低延迟
    • G1 收集器:区域化分代

垃圾收集算法

垃圾的概念

  在运行的程序中,当一个对象没有任何指针指向它时,它就会被视为垃圾。所以,判断一个对象是否被视为垃圾的关键标准就是是否由指针指向它。
进行垃圾回收有两个好处:
  1、释放内存,防止oom的发生。
  2、规整内存,提高内存的使用率。垃圾回收后,会出现零散的空位,这时候进行规整,可以得到一块连续的存储空间。

对象存活的判断

引用计数器法

  引用计数器法比较简单,每个对象保存一个整形的引用计数器属性,用于记录对象被引用的次数,只要有任何一个对象引用了该对象,该对象的引用计数器就加一,当引用失效后,引用计数器就减一,当该对象的引用计数器为0时,就表明该对象不再被引用,可以进行回收了。使用引用计数器比较简单,但是有三个缺点:
  1、每个对象需要单独的字段存储计数器,增加了空间开销。
  2、每次操作都需要增加对象的计数器,增加了时间开销。
  3、无法处理循环问题,对象A引用了对象B,对象B也引用了对象A,则这两个对象永远无法被回收。

可达性分析算法

  可达性分析算法以GC Root为起点,从上至下,找到被根对象所连接的目标对象,但是如果目标没有在引用链上,则表示对象时不可达的,这些对象也就被视为垃圾对象。GC Root 有多个,他们组成了 GC Roots,作为GC Roots 的对象引用包括下面几种类型:
  1、虚拟机栈中对象的引用,比如方法中的局部变量。
  2、本地方法栈中对象的引用。
  3、方法区中引用数据类型的静态变量。
  4、方法区中常量的引用,比如字符串常量池中的引用。
  5、被同步锁持有对象的引用
  6、jvm内部的引用,比如基本类型对应的Class对象引用,一些常驻的异常对象引用,系统类加载器对象应用。

算法

下面总结一下jvm中常用的三种算法:

标记清除算法

  标记清除算法是非常基础的一种算法。当堆中有效的内存空间被耗尽的时候,就会停止程序,然后进行两项工作:
  1、标记:使用可达性分析算法,从根节点开始遍历,标记所有被引用的对象。
  2、清除:垃圾收集器再次进行遍历,如果发现某个对象不可达,则将其回收掉。

  标记清除算法的优点是简单、且不需要移动对象,但缺点是在进行GC时,需要停止整个应用程序,且清理出来的内存是不连续的,会产生内存碎片。且该方式并不是真正的清除不可达的对象,而是将要被清除的对象地址保存在空闲的列表中,新对象申请内存时,判断某块内存是否充足,如果充足的话,就将新对象覆盖原来标记为垃圾的对象,从而实现内存的重复利用,但是如果是大对象申请时,需要花费更多时间去寻找合适的位置,甚至失败,所以标记清除算法效率不高,且部分内存碎片无法重复利用。

复制算法

  复制算法的核心思想是将空闲的内存空间分为两块,但每次只使用其中的一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,然后在清除正在使用内存块中的对象,两块内存交替使用,从而完成垃圾对象的回收。
  虽然复制算法不需要进行标记,也可以保证内存的连续性,但该算法需要双倍的内存,而且需要移动对象,这就意味着需要更改对象的引用地址。如果内存中存活的对象很多,该复制算法不会很理想,因为需要复制对象的数量变大,使垃圾回收器运行效率变低。综上所述,复制算法更适用于新生代。

标记压缩算法

  在老年代中,更多的对象是存活的,不太适合使用复制算法,但可以使用标记压缩算法,它是基于标记清除算法的演进,执行流程主要分为三步:
  1、从根节点标记被引用的对象,这点和标记清除算法一致。
  2、将所有存活对象压缩到一端,按顺序排放。
  3、清除边界外的所有空间。
  其优点是避免了内存碎片,缺点是因为需要移动对象,所以需要修改对象引用地址。

垃圾收集的相关概念

STW

  在垃圾回收过程中,整个应用程序都会被暂停,没有任何响应,所以被称为Stop The World,简称为 STW。
  可达性分析算法在枚举根节点时,会造成STW,原因是如果分析过程中出现对象引用关系的变化,无法导致分析结果的准确性,当GC完成之后,STW也会停止,且STW与垃圾收集器没有关系,任何垃圾回收器都会都会发生STW。

安全点

  上面说到,垃圾回收过程中会进行STW,但是在垃圾回收时,并不是任何位置都适合停顿下来进行GC的,只有特定的位置才能停顿下来进行GC,这些位置被称为安全点(Safe Point)。
  安全点如果过少,会导致GC等待的时间过长,但是安全点太多会导致性能问题,一些常见的安全点包括:循环的结尾、方法返回前、调用方法之后、异常抛出的位置。
  当发生GC时,保证程序的线程落在安全点有两种方式:
  1、抢先式中断:GC中断所有线程,如果发现某些线程不在安全点,就重新恢复该线程,直到它跑到安全点,在进行停止。因为这种方式是GC主导位置的,导致应用程序并不是主角,所以几乎所有的虚拟机都不选择这种方式。
  2、主动式中断:GC线程给自己设置一个标志位,各个应用线程运行到安全点来查询这个标志,如果此时的GC线程的中断标志为真,则将自己中断挂起,这种方式的好处是由应用线程主动发起中断,不会被迫出现在非安全点位置中断的情况。

安全区域

  当某些线程被阻塞了,无法走到安全点,也就无法响应jvm的中断请求,为了解决这个问题,引入了安全区域的概念。
  安全区域指的是在一段代码片段中,对象的引用关系不会发生变化,在这个区域中的任何位置开始GC都是安全的。
  线程对于安全区域的处理如下:
  1、当线程走到安全区域的代码时,首先已经标识进入了安全区域,如果这段时间内发生GC,jvm会忽略在安全区域的线程,忽略的意思是说如果除了在安全区域内的其他线程到达了安全点,就可以进行GC了。
  2、当线程离开安全区域,会检查jvm是否已经完成了GC,如果完成了则继续执行,否则就阻塞,直到GC完成之后才继续执行。
  从JVM角度出发,安全区域是放大了的安全点,都是可以进行GC的位置。但是从应用线程来看,这两个位置本质不同,安全点是应用线程自己挂起自己的地方,安全区域是应用线程无需挂起的地方(只是在出安全区域的时候才会挂起)

垃圾收集器

  上面讲的找到垃圾的算法是垃圾回收算法的基础,垃圾回收算法又是垃圾收集器的基础。下面来总结一下垃圾收集器。

重要指标

  对于垃圾收集器来说,有两个重要的指标,一个是吞吐量,一个是停顿时间。

吞吐量

  吞吐量是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。比如垃圾回收了2次,每次200ms,CPU总耗时是6000ms,则吞吐量=(6000-400)/6000=93.33%。
在这里插入图片描述

停顿时间

  停顿时间是指一个时间段内应用线程暂停,只让垃圾收集线程执行的时间。比如下图中,每次垃圾回收停顿100ms,总共停顿5次,一共500ms,虽然和上图比总的停顿时间变长了,但是每次停顿的时间减少了,整体看,应用程序的延迟是比较低的。但此时程序的吞吐量=(6000-500)/6000=91.67%。
在这里插入图片描述
  综上所述,高吞吐量和低停顿时间不能同时满足。如果选择吞吐量优先,那么就需要降低内存回收的次数,这样会导致垃圾收集需要更长的时间,用户的体验感会下降。如果以低停顿时间优先,每次停顿时间降低了,但回收次数变多了,就会引起吞吐量的下降。

垃圾收集器的分类

  根据垃圾收集器工作的内存区间不同,可以分为新生代垃圾收集器,老年代垃圾收集器和整堆垃圾收集器。
  新生代垃圾收集器:Serial、ParNew、Parallel Scavenge
  老年代垃圾收集器:Serial Old、Parallel Old、CMS
  整堆垃圾收集器:G1
在这里插入图片描述
  按照线程工作方式划分,也可以划分为三种:
  串行收集器:Serial、Serial Old
  并行收集器:ParaNew、Parallel Scavenge、Parallel Old
  并发收集器:CMS、G1

  串行垃圾收集器是指单线程收集垃圾,即使会存在多个cpu可用,但也只能用一个cpu进行垃圾回收。
  并行收集器是指多个垃圾收集线程并行工作,当存在多个cpu可用时,并行垃圾回收器就会使用多个cpu进行回收。
  并发收集器是指用户线程和垃圾收集线程“同时”进行,也就是可能存在交替执行的状况,当有多个cpu或者存在多个cpu核的情况时,用户线程和垃圾收集线程就会并行执行。虽然叫并发收集器,但是还是可能会存在并行的情况。

Serial 收集器:串行回收

  Serial 收集器时是新生代的垃圾收集器,采用的是复制算法、串行回收。此外,Serial 还提供了老年代的垃圾收集器 Serial Old 收集器,它采用的是标记压缩算法,并且它还是CMS收集器的备用方案(下文会说到)。该收集器已经几乎不存在了,因为它是串行,所以一般会用在单核CPU的环境下。

ParNew 收集器:并行回收

  ParNew就是Serial的多线程版本,除了采用多线程进行回收之外,它和Serial 没有任何区别。
在多CPU的情况下,ParNew 的效率高于Serial,但是在单线程情况下,Serial的效率会更高一些,因为此时减少了线程切换的开销。

Parallel Scavenge 收集器:吞吐量优先

  Parallel Scavenge 收集器也采用了并行回收、复制算法的机制,但是他和ParNew的区别在于他拥有自适应调节策略,他支持动态的调整参数以提供最合适的停顿时间或最大的吞吐量。并且它是jdk8的默认收集器。
  在吞吐量优先的业务中,Parallel Scavenge(新生代) 和 Parallel Old(老年代)收集器组合使用,可以达到不错的回收性能。

CMS收集器:低延迟

  CMS是一款并发收集器(不是并行),可以让垃圾收集线程和用户线程同时工作。它更关注的是如何在垃圾回收时,用户线程的停顿时间变短,可以为用户提供良好的体验。
CMS的工作原理主要分为4个步骤:
  1、初始标记。第一步就是利用可达性分析算法,找出所有的GC Roots直接关联的对象,为了一次性找齐全,需要进行STW,等找完之后就会恢复用户线程。
  2、并发标记。上一步是找到了GC Roots直接关联的对象,这一步是沿着链继续查找所有的关联对象,该步骤不需要STW,可以和用户线程并发进行。因为它和用户线程并发进行的,也就会导致找到的对象不是很准确,可能标记了一个对象为非垃圾对象,因为此时用户线程正在使用该对象;然后该对象没有引用了,变成了垃圾对象,但是在CMS看来,该对象还是存活状态的,可以理解为此时的并发标记是一次快照。但是如果一次性找正确,为啥不进行STW呢,第三部解释了原因。
  3、重新标记。为了解决上述并发标记的问题,CMS在此阶段需要重新标记引用不正确的对象。但是为了防止此阶段再次出现引用不正确的现象,需要进行STW。可以看出CMS在此处选择STW还是很有道理的,因为此时引用不正确的对象肯定小于第二步并发标记的对象,所以选择在此步骤进行STW,不在第二步STW,提高了用户体验感。
  4、并发清除。清除标记为死亡状态的对象,可以和用户线程并发进行。但是在清除的阶段还是会产生浮动垃圾。
  因为CMS在并发标记和并发清除两个阶段不需要STW,所以停顿时间相对较短,但是它也存在一定的缺点:
  1、会产生内存碎片。因为CMS是基于标记-清除算法的,所以会产生内存碎片,遇到分配大对象的情况时,需要提前进行Full GC。
  2、产生浮动垃圾。在并发标记和并发清除阶段,因为是和用户线程并发进行的,所以用户线程还是会产生浮动垃圾,产生的这些垃圾只能等到下次GC再进行清除。
  3、对CPU比较敏感。因为它是和用户线程并发进行,会在一定程度上影响用户线程的吞吐量。
  4、如果在并发清除阶段,用户线程的内存不足,就会采用备用方案,启动Serial Old收集器进行一次Full GC。

G1 收集器:区域化分代

  G1(Garbage first)收集器是一款基于并发和并行的垃圾收集器,它把堆分割成很多个大小相等的区域(region),一般大小控制在1~32M,为2的N次幂。每个区域可能是老年代也可能是新生代。所以它和传统的收集器不同,并不是直接将堆划分为新生代和老年代两部分。
在这里插入图片描述
  如上图所示,每个region都可能是Eden、S、Old区,除此之外,G1中还存在Humongous区域,大小一般超过region的1.5倍,设置H区的原因是防止因region区放不下大对象而提前进行Full GC。它和CMS不同的是,每一个region区域进行分配对象的方式为指针碰撞,而CMS为引用列表的方式。这也说明了G1收集器不会产生内存碎片。
  它可以根据每个region区域被回收后释放的大小和所需时间来决定优先回收哪个,所以叫做Garbage first。
它的回收流程如下:
一、新生代GC。
  jvm启动时,不断的创建Eden区,然后将存活的对象放入到Eden区中,当Eden区耗尽时,会进行一次GC,然后回收Eden和S区。
  GC过程如下:
  1、扫描根。找到GC Roots直接关联的对象。
  2、更新Rset。Rset指的是如果一个region 引用其他region中对象,就在当前region中记录一个集合,用于存放相关引用对象所在的region。
  3、处理Rset。如果年轻代的对象被老年代引用,则这些对象属于是存活对象。
  4、复制对象。将存活的对象复制到S区,然后年龄加一,如果达到了年龄的阈值,就复制到Old区。
  5、处理引用。主要处理的是一些软引用、弱引用,最终Eden中的数据为空,此时目标内存中的数据都是连续的,没有产生内存碎片。
二、并发标记。
  上述主要进行了年轻代的GC,这次主要处理老年代。
  1、初始标记阶段:标记根节点可达的对象,此阶段会进行STW。
  2、根区域扫描。找到S区中直接引用的Old区中的对象,并进行标记。
  3、并发标记。在整个堆中进行并发标记找到存活的对象,该过程和用户线程是并发执行的。如果在此阶段发现有的region区域全是垃圾,则直接进行回收。
  4、再次标记。此阶段会进行STW,和CMS的重新标记一样。
  5、独占清理。此阶段不进行回收,只是找出可以混合回收的区域(一个region可以回收一部分的区域。)
  6、并发清理。此阶段找到完全空闲的区域,并进行清除。
三、混合回收。
  并发标记结束后,只会清除老年代中百分百为垃圾的区域,上面的第5步还会存在一些混合区域。于是此阶段会进行回收一部分老年代+一部分年轻代,回收过程和新生代GC过程一致。

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

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

相关文章

数据结构——查找(线性表的查找与树表的查找)

目录 1.查找 1.查找的基本概念 1.在哪里找? 2.什么查找? 3.查找成功与否? 4.查找的目的是什么? 5.查找表怎么分类? 6.如何评价查找算法? 7.查找的过程中我们要研究什么? 2.线性表…

【周末闲谈】Stable Diffusion会魔法的绘画师

个人主页:【😊个人主页】 系列专栏:【❤️Python】 文章目录 前言Stable Diffusion介绍 使用ComfyUI 和 WebUIComfyUIWebUI 配置需求 Stable Diffusion资源分享吐司AiAUTOMATIC1111Civitai绘世整合包Nenly同学stability.ai 前言 在很早之前&…

2-33 基于matlab的用于计算无故障的斜齿轮对啮合时接触线长度随时间的变化

基于matlab的用于计算无故障的斜齿轮对啮合时接触线长度随时间的变化,根据需求设置斜齿轮对的相应参数,得到结果。程序已调通,可直接运行。 2-33 斜齿轮对啮合时接触线长度 齿轮参数 - 小红书 (xiaohongshu.com)

【笔记】nginx命令

查看 启动 通过./nginx启动nginx之后 可以在虚拟机中进入/usr/local/nginx/html 去查看cat index.html 也就是此页面的源代码 进入vim /etc/profile 配置完之后保存退出 source /etc/profile 手动重载资源 随后就可以在任意位置重载资源了 nginx -s reload 部署静态资源就把静…

【Linux】进程程序替换 + 模拟实现简易shell

前言 上一节我们介绍了 **进程终止**和 **进程等待**等一系列问题,并做了相应的验证,本章将继续对进程控制进行介绍,重点学习进程程序替换,并进行相应验证,在此基础上,自己模拟实现一个shell,该…

前端web性能统计

前端web性能统计 1. 背景2. 业界方案2.1 腾讯2.2 蚂蚁金服2.3 字节跳动2.4 美团 3. 相关观念3.1 RAIL模型3.2 性能指标3.3 真实用户监控3.4 performance 4. 性能监控工具介绍5. 推荐采用方案 1. 背景 在如今的数字时代,网站和应用程序的性能对用户体验至关重要。用…

机器人相关工科专业课程体系

机器人相关工科专业课程体系 前言传统工科专业机械工程自动化/控制工程计算机科学与技术 新兴工科专业智能制造人工智能机器人工程 总结Reference: 前言 机器人工程专业是一个多领域交叉的前沿学科,涉及自然科学、工程技术、社会科学、人文科学等相关学科的理论、方…

FOC(笔记二)

接上篇文章:FOC算法(笔记一)_马鞍波和三角波调制合成-CSDN博客 前面已经对FOC的开环控制进行了介绍,下面对FOC的闭环控制进行介绍。 本次使用的电机参数如下图所示: 一、HALL传感器 1.1、霍尔传感器的角度、速度计算 因为本次使用的是120安…

SpringCloud02_consul概述、功能及下载、服务注册与发现、配置与刷新

文章目录 ①. Euraka为什么被废弃②. consul简介、如何下载③. consul功能及下载④. 服务注册与发现 - 8001改造⑤. 服务注册与发现 - 80改造⑥. 服务配置与刷新Refresh ①. Euraka为什么被废弃 ①. Eureka停更进维 ②. Eureka对初学者不友好,下图为自我保护机制 ③. 阿里巴巴…

taro小程序terser-webpack-plugin插件不生效(vue2版本)

背景 最近在做公司内部的小程序脚手架,为了兼容老项目和旧项目,做了vue2taro,vue3taro两个模板,发现terser-webpack-plugin在vue2和vue3中的使用方式并不相同,同样的配置在vue3webpack5中生效,但是在vue2webpack4中就…

【Linux】:重定向和缓冲区

朋友们、伙计们,我们又见面了,本期来给大家带来关于重定向和缓冲区的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏:C语言:从入门到精…

【题解】 栈和排序(栈 + 预处理 / 贪心)

https://www.nowcoder.com/practice/95cb356556cf430f912e7bdf1bc2ec8f?tpId196&tqId37173&ru/exam/oj 预处理最大值 #include <climits> // 包含标准整数类型的定义 #include <vector> // 包含标准vector容器的定义class Solution {public:/*** 栈排…

接着探索Linux的世界 -- 基本指令(文件查看、时间相关、打包压缩等等)

话不多说&#xff0c;直接进入主题 一、cat指令 -- 查看目标文件的内容 语法&#xff1a;cat [选项][文件] 功能&#xff1a; 查看目标文件的内容 -b 对非空输出行编号 -n 对输出的所有行编号 -s 不输出多行空行 1、查看目标文件的内容 2、 -b 对非空输出行编号 3、-n 对…

论文浅尝 | 学会使用上下文学习来进行命名实体识别

笔记整理&#xff1a;王润哲&#xff0c;东南大学硕士&#xff0c;研究方向为大模型 链接&#xff1a;https://aclanthology.org/2023.acl-long.764.pdf 1. 动机 实体关系是知识图谱中不可或缺的一层重要信息&#xff0c;它们描述了实体之间的语义关系&#xff0c;这种连接使得…

【填坑指南】PHP8报:Unable to load dynamic library ‘zip.so’ 错误

1.原因分析 这种情况多数发生在PHP安装时因为各种原因失败后&#xff0c;残余的库与最后安装的PHP版本不兼容导致的。 2.我的路径 一开始我按照以前摸索出来的安装PHP7.3的成功经验来编译方法安装PHP8.3&#xff0c;发现以前的套路已经失效了。反复重装PHP8.3失败后&#xf…

Visual Studio 安装程序无法执行修复或更新

一.问题场景 出现问题的场景&#xff1a;当你的VS已经安装但是无法在工具中下载新组件或者卸载了当时一直无法安装。 二.问题原因 如果计算机上的 Visual Studio 实例已损坏&#xff0c;则可能会出现此问题。 三.解决方法 如果之前尝试修复或更新 Visual Studio 失败&…

Qt5离线安装包无法下载问题解决办法

Qt5离线安装包无法下载问题解决办法 文章目录 Qt5离线安装包无法下载问题解决办法1、前言2、Qt5安装包下载办法 更多精彩内容&#x1f449;个人内容分类汇总 &#x1f448;&#x1f449;Qt开发经验 &#x1f448; 1、前言 Qt安装包官方下载地址 Qt5离线安装包目前在国内已经被墙…

Meta即将推出4000亿的Llama 3 超级AI模型,或将改写大语言模型竞争格局!|TodayAI

2024年4月&#xff0c;科技巨头Meta发布了其最新的AI大型语言模型——Llama 3&#xff0c;该模型基于一个至少比前代产品Llama 2大七倍的数据集&#xff0c;展现出前所未有的性能。在最初发布时&#xff0c;Llama 3提供了8B和70B两种参数规模的版本&#xff0c;并迅速超越了Goo…

SpringBoot新手快速入门系列教程十一:自动生成API文档,Springboot3.x集成SpringDoc

本次项目我们用Maven来做&#xff0c;最近发现gradle其实很多项目的支持比较差&#xff0c;所以项目还是用Maven来新建项目。对比了市面上的几种API生成第三方库&#xff0c;只有springdoc 是能够按照文档就能部署出来的。 官网&#xff1a; OpenAPI 3 Library for spring-bo…

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(八)-通过无人机进行无线接入

引言 本文是3GPP TR 22.829 V17.1.0技术报告&#xff0c;专注于无人机&#xff08;UAV&#xff09;在3GPP系统中的增强支持。文章提出了多个无人机应用场景&#xff0c;分析了相应的能力要求&#xff0c;并建议了新的服务级别要求和关键性能指标&#xff08;KPIs&#xff09;。…