JVM的垃圾回收算法有哪些?从可达性分析算法开始,深入解读三大核心垃圾回收算法

news2024/11/26 8:55:50

导航:

【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/黑马旅游/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码-CSDN博客 

目录

一、概念准备

1.1 GC Roots

1.2 可达性分析算法

1.3 非可达对象被回收过程中的两次标记

1.4 finalize()方法

二、垃圾回收算法

2.1 标记清除算法(Mark-Sweep)

2.2 标记复制算法(Copying) 

2.3 标记整理算法(Mark-Compact) 

2.4 小结:三种垃圾回收对比

三、分代收集理论


 

一、概念准备

1.1 GC Roots

GC Roots:即GC根节点集合,是一组必须活跃的引用,这些对象一定是存活的,不需要被垃圾回收器回收。GC即Garbage Collection,译为垃圾回收。

可作为GC Roots的对象:

  • 本地方法栈中引用的对象:即由本地方法中的局部变量所引用的对象。本地方法栈用于管理本地方法的调用,这些本地方法底层是用C语言写的,被编译为基于本机硬件和操作系统的程序,它们引用的对象不会被回收,所以是GC Root。
  • 虚拟机栈中引用的对象:虚拟机栈又称Java方法栈,即由 Java 方法中的局部变量所引用的对象。虚拟机栈中的局部变量、参数以及返回值等都是直接引用对象的,他们是明确的、在程序运行期间被访问到的对象,所以它们也是GC Root。
  • 方法区中常量、静态变量引用的对象:常量不可变、静态变量先于对象产生,被所有该类的对象共享,所以它们都可认为是GC Root。final修饰的类不可被继承、方法不可被重写、变量不可变。静态变量是类变量,优先于对象产生。常量、静态变量都存放在JVM方法区的类常量池中,类常量池在类加载阶段从Java字节码文件中解析。
  • 所有被同步锁持有的对象:在 Java 虚拟机中,线程持有同步锁时,该对象就是被线程所引用的,即使在程序的其他部分没有对该对象进行引用,它也不会被回收。
  • 所有线程对象:线程对象是程序执行的基本单位之一,他们被创建后,会一直存在直到它们被显式地销毁或者程序运行结束。
  • 所有跨代引用对象:年轻代和老年代之间相互引用的对象成为跨带引用对象。
  • JVM内部的引用:如基本数据类型对应的Class对象,常驻的异常对象(如空指针异常、参数不合法异常,他们在JVM启动过程中就被加载,因为它们太常用了),以及应用程序类类加载器; 

回顾JVM内存模型:

对JVM内存模型(方法区、堆、栈等元素) 不熟悉,可以参考下文:

什么是JVM的内存模型?详细阐述Java中局部变量、常量、类名等信息在JVM中的存储位置-CSDN博客

 

1.2 可达性分析算法

以所有GC Roots为起始点,根据引用关系向下搜索,将所有与GC Roots直接或间接有引用关系的对象在对象头的Mark Word里标记为可达对象,即不需要回收的有引用关系对象。搜索过程所走过的路径称为“引用链” 。

例如下面对象1,2,3,4都在GC Roots的引用链上,所以它们都是可达对象,不会被回收。而对象5,6,7因为没有跟任何一个GC Root直接或间接产生调用关系,所以它们是非可达对象,需要被回收。

 

1.3 非可达对象被回收过程中的两次标记

非可达对象被回收需要两次标记:

  1. 第一次标记后筛选非可达对象:
    1. 非可达对象第一次被可达性分析算法标记后,会进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法,也就是是否有机会自救。
    2. 假如对象没有覆盖或者已被JVM调用过finalize()方法,也就是说不想自救或已自救过,那么此对象需要被回收;
    3. 假如对象覆盖并没被JVM调用过finalize()方法,该对象将会被放置在一个名为F-Queue的队列之中,并在稍后由一条由虚拟机自动建立的、低调度优先级的Finalizer线程去执行它们的finalize()方法,完成自救后它们就不会被回收。
  2. 第二次标记F-Queue里的未自救对象:
    1. 稍后,收集器将对F-Queue中的对象进行第二次小规模的标记。
    2. 如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this)赋值给某个引用类型的类变量或者对象的成员变量,那在第二次标记时它将被移出“即将回收”的F-Queue。如果对象这时候还没有逃脱,那它就真的要被回收了。

1.4 finalize()方法

Java中,任何对象都直接或间接是Object类的子类,finalize()是Object类中的一个方法,所以可以说,Java中任何类都有一个finalize()方法。finalize()方法是对象逃脱死亡命运的最后一次机会。

需要注意的是,任何一个对象的finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()方法不会被再次执行。

另外,finalize()方法的运行代价高昂,不确定性大,无法保证各个对象的调用顺序,如今已被官方明确声明为不推荐使用的语法。

finalize()方法所在位置:

二、垃圾回收算法

2.1 标记清除算法(Mark-Sweep)

步骤: 

  1. 标记、清除:当堆中有效内存空间被耗尽时,会STW(stop the world,暂停其他所有工作线程),然后先标记,再清除。
  2. 标记:可达性分析法,从GC Roots开始遍历,找到可达对象,并在对象头中进行标记。
  3. 清除:堆内存内从头到尾进行线性遍历,“清除”非可达对象。注意清除并不是真的置空,垃圾还在原来的位置。实际是把垃圾对象的地址维护在空闲列表,对象实例化的申请内存阶段会通过空闲列表找到合适大小的空闲内存分配给新对象。

 

对象头: Hotspot 虚拟机中每个对象都有一个对象头(Object Header),包含Mark Word(标记字段) 和 Class Pointer(类型指针)。

  • Mark Word(标记字段):存储哈希码、GC分代年龄、锁信息、GC标记(标志位,标记可达对象或垃圾对象)等。锁信息包括:
    • 锁标志位:64位的二进制数,通过末尾能判断锁状态。01未锁定、01可偏向、00轻量级锁、10重量级锁、11垃圾回收标记
    • 偏向锁线程ID、时间戳等;
    • 轻量级锁的指针:指向锁记录的指针
    • 重量级锁的指针:指向Monitor锁的指针
  • 类型指针:指向它的类元数据的指针,用于找到对象所在的类。  

 

优缺点和使用场景:

  • 优点:简单
  • 缺点:
    • 效率不高:需要可达性遍历和线性遍历,效率差。
    • STW导致用户体验差:GC时需要暂停其他所有工作线程,用户体验差。
    • 有内存碎片,要维护空闲列表:回收垃圾对象后没有整理,导致堆中出现一块块不连续的内存碎片。
  • 适用场景:适合小型应用程序,内存空间不大的情况。应用程序越大越不适用这种回收算法。

 

2.2 标记复制算法(Copying) 

在开始阶段,将内存空间分为两块,每次只使用一块。 

步骤:  

  • 标记:在进行垃圾回收时,先可达性分析法标记可达对象。
  • 复制:然后将可达对象复制到没有被使用的那个内存块中;
  • 清除:最后清除旧内存块中的所有对象。、
  • 后续再按同样的流程来回复制和清除。

 

优缺点和使用场景:

  • 优点:
    • 垃圾多时效率高:只需可达性遍历,效率很高。
    • 无内存碎片:因为有移动操作,所以内存规整。
  • 缺点:
    • 内存利用率低,浪费内存:始终有一半以上的空闲内存。
    • 需要调整引用地址:可达对象移动后,内存地址发生了变化,需要调整所有引用,指向移动后的地址。
    • 垃圾少时效率相对差,但还是比其他算法强:如果可达对象比较多,垃圾对象比较少,那么复制算法的效率就会比较低。只为了一点垃圾而移动所有对象未免有些小题大做。所以垃圾对象多的情况下,复制算法比较适合。
  • 适用场景:
    • 适合垃圾对象多,可达对象少的情况,这样复制耗时短。
    • 非常适合新生代的垃圾回收,因为新生代要频繁地把可达对象从伊甸园区移动到幸存区,而且是新生代满了适合再Minor GC,垃圾对象占比高,所以回收性价比非常高,一次通常可以回收70-90%的内存空间,现在的商业虚拟机都是用这种GC算法回收新生代。

2.3 标记整理算法(Mark-Compact) 

步骤:   

  • 标记:首先可达性分析法标记可达对象;
  • 整理:然后将可达对象按顺序整理到内存的一端;
  • 清除:最后清理边界外的垃圾对象。

标记整理算法相当于内存碎片优化版的标记清楚算法,不用维护空闲列表。

 

优缺点和使用场景:

  • 优点:
    • 无内存碎片:内存规整。
    • 内存利用率最高:内存既规整又不用浪费一般空间。
  • 缺点:
    • 效率最低:效率比其他两种算法都低
    • 需要调整引用地址:可达对象移动后,内存地址发生了变化,需要调整所有引用,指向移动后的地址。
    • STW导致用户体验差:移动时需要暂停其他所有工作线程,用户体验差。

2.4 小结:三种垃圾回收对比

标记清除算法标记复制算法标记整理算法
速度中等最快最慢
时间开销mark阶段与存活对象的数量成正比,sweep阶段与整堆大小成正比与存活对象大小成正比mark阶段与存活对象的数量成正比,compact阶段与整堆大小成正比,与存活对象的大小成正比
空间开销少(但会堆积碎片)通常需要存活对象的2倍大小(不堆积碎片)少(不堆积碎片)
移动对象

 

三、分代收集理论

垃圾回收器都不会只选择一种算法,JVM根据对象存活周期的不同,将内存划分为几块。一般是把堆分为新生代和老年代,根据年代的特点来选择最佳的收集算法。

分代收集算法:将堆分为新生代、老年代不同生命周期的对象放在不同的代,采用不同的收集算法,以提高回收效率。 

HotSpot 中大部分垃圾回收器都采用分代回收的思想,即新生代用一种垃圾回收算法,老年代用一种垃圾回收算法。

以JDK8为例,JDK8默认回收器是Parallel+Parallel Old,新生代用Parallel回收器,老年代用Parallel Old回收器。

回收过程:

  1.  首先,任何新对象都分配到 eden 空间。两个幸存者空间开始时都是空的。
  2. 当 eden 空间填满时,将触发一个Minor GC(年轻代的垃圾回收,也称为Young GC),删除所有未引用的对象,大对象(需要大量连续内存空间的Java对象,如那种很长的字符串)直接进入老年代。
  3. 所有被引用的对象作为存活对象,将移动到第一个幸存者空间S0,并标记年龄为1,即经历过一次Minor GC。之后每经过一次Minor GC,年龄+1。GC分代年龄存储在对象头的Mark Word里。
  4. 当 eden 空间再次被填满时,会执行第二次Minor GC,将Eden和S0区中所有垃圾对象清除,并将存活对象复制到S1并年龄加1,此时S0变为空。
  5. 如此反复在S0和S1之间切换几次之后,还存活的年龄等于15的对象(JDK8默认15,JDK9默认7,-XX:InitialTenuringThreshold=7)在下一次Minor GC时将放到老年代中。 
  6. 当老年代满了时会触发Major GC(也称为Full GC),Major GC 清理整个堆 – 包括年轻代和老年代。

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

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

相关文章

【数据结构与算法 刷题系列】环形链表的约瑟夫问题

💓 博客主页:倔强的石头的CSDN主页 📝Gitee主页:倔强的石头的gitee主页 ⏩ 文章专栏:数据结构与算法刷题系列(C语言) 目录 一、问题描述 二、解题思路详解 解题思路 解题步骤 三、C语言代码…

文件流-ASCII文件(中北大学-程序设计基础(2))

目录 题目 源码 结果示例 题目 编写程序实现以下功能:【要求处理ASCII文件】 (1)按职工号由小到大的顺序将5个员工的数据(包括号码、姓名、年龄和工资)输出到磁盘文件中保存; (2&#xff…

服务器3389端口,服务器3389端口风险提示的应对措施

3389端口是Windows操作系统中远程桌面协议(RDP)的默认端口。一旦该端口被恶意攻击者利用,可能会导致未经授权的远程访问和数据泄露等严重安全问题。 针对此风险,强烈建议您采取以下措施: 1. 修改默认端口:…

八、e2studio VS STM32CubeIDE之内存使用情况窗口

目录 一、概述/目的 二、STM32CubeIDE Build Analyzer 三、e2studio Memory Usage 八、e2studio VS STM32CubeIDE之内存使用情况窗口 一、概述/目的 1、嵌入开发最大特点之一就是资源受限,关注芯片资源使用详情是优秀工程师的技能之一 2、Keil和IAR都不支持内存…

Linux性能压测指标信息

1、CPU使用 服务器CPU整体负载信息 可以查看top命令输出的第三行数据 查看多核CPU命令 mpstat -P ALL 和 sar -P ALL top命令执行后输入1 2、内存使用 top命令或者free命令来查看内存的信息,第一行是物理内存使用,第二行是虚拟内存使用(交换空间)。…

WebSocket建立网络连接——小案例

WebSocket是一种实现全双工通信的网络技术标准,它允许在用户的浏览器和服务器之间进行持久的、双向的通信。以下是对WebSocket的具体介绍: 实时性:与传统HTTP请求相比,WebSocket提供了更高效的实时数据交换方式。一旦建立连接&am…

【cpp】并发多线程 Unique

1. unique_lock 何时锁定资源。 unique_lock lock1 时候&#xff0c;还没有锁住资源。 实际是后面&#xff0c;显式的出发&#xff1a; 比如&#xff0c; lock.lock, 或 std::lock(lk1,lk2), 或者条件变量CV.wait(mtx, []{!re})。 #include <iostream> #include <mu…

虚拟仿真云平台在教育应用中的优势和意义

虚拟仿真云实验教学平台作为一种新型的教学方法&#xff0c;近年来在高校教育中得到了十分广泛的应用。它通过模拟真实的实验场景和实验操作&#xff0c;让学生在计算机上进行实验操作和数据处理&#xff0c;为学生提供了更加便捷、可靠、有效的实验学习环境。本文&#xff0c;…

ssm123基于java web的网上书城系统的设计与实现+vue

基于java web的网上书城系统的设计与实现vue 摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff0c;商品交易当然也不能排除在外&#xff0c;随着商品交易管理的不断成熟&#xff0c;它彻底改变了…

基于SpringBoot + Vue实现的考编(考研)论坛网站设计与实现+毕业论文

系统介绍 系统包含用户和管理员两个角色 用户&#xff1a;登录、注册、经验交流平台、发布帖子、回复帖子、查看公告信息、跳蚤市场&#xff08;商品购买、商品出售、商品评价&#xff09;、个人中心等功能 管理员&#xff1a;登录、个人中心、管理员管理、用户管理、基础数据管…

REACT 在组件之间共享状态

有时&#xff0c;您希望两个组件的状态始终一起变化。要做到这一点&#xff0c;请从他们俩身上删除状态&#xff0c;将其移动到他们最近的共同父级&#xff0c;然后通过道具将其传递给他们。这被称为提升状态&#xff0c;这是编写 React 代码时最常见的事情之一。 举例提升状态…

2024年3月 青少年等级考试机器人理论真题三级

202403 青少年等级考试机器人理论真题三级 第 1 题 流程图图例如下&#xff0c;与该图例功能对应的选项是&#xff1f;&#xff08; &#xff09; A&#xff1a;开始/结束 B&#xff1a;输入/输出 C&#xff1a;判断 D&#xff1a;处理 第 2 题 Arduino UNO/Nano主控板&am…

机器人学导论实验1—CoppeliaSim 平台介绍及初步使用BJTU

1. 实验内容分析 对实验内容的理解及关键点&#xff1a; 理解这个实验的关键点在于理解如何使用CoppeliaSim和MATLAB来控制和操作机器人。需要熟悉这两个工具的基本操作&#xff0c;例如如何加载场景、如何修改机器人参数、如何使用MATLAB客户端程序来控制机器人等。此外&#…

开源分布式爬虫管理平台:性能强悍!!【送源码】

简介 基于 Golang 的分布式爬虫管理平台&#xff0c;支持 Python、NodeJS、Go、Java、PHP 等多种编程语言以及多种爬虫框架。 谁适合使用 Crawlab? 网路爬虫工程师&#xff1a; 通过集成爬虫程序到 Crawlab&#xff0c;网路爬虫工程师可以聚焦于爬虫的核心解析逻辑&#xff0…

C语言学习【C语言基本数据类型二】

C语言学习【C语言基本数据类型二】 _Bool类型 C99标准添加了_Bool类型&#xff0c;用于表示布尔值&#xff0c;即逻辑值true和false&#xff0c;原则上仅占用1位存储空间&#xff1b; float、double和long double 记数法示例 C标准规定&#xff0c;float类型必须至少能表示…

2024CCPC郑州站超详细题解(含题面)ABFHJLM(河南全国邀请赛)

文章目录 前言A Once In My LifeB 扫雷 1F 优秀字符串H 随机栈J 排列与合数L Toxel 与 PCPC IIM 有效算法 前言 这是大一博主第一次参加xcpc比赛&#xff0c;虽然只取得了铜牌&#xff0c;但是收获满满&#xff0c;在了解了和别人的差距后会更加激励自己去学习&#xff0c;下面…

Linux修炼之路之权限

目录 引言 一&#xff1a;Linux中用户的分类 二&#xff1a;在Linux中的权限 1.权限的两种属性 1.人的属性 2.事物属性 -主要以文件属性为主 3.文件权限值的两种表示方式方法 2.更改文件访问者(拥有者&#xff0c;所属组&#xff0c;其他人)权限属性 3.更改文件的拥有…

vue3专栏项目 -- 四、前后端结合(下)

一、async 和 await 1、使用async 和 await 改造异步请求 在接触后端API以后就遇到了越来越多的异步请求&#xff0c;现在我们就使用async 和 await 改造异步请求。 async function是把返回内容包裹成个Promise返回Promise await 它在async function里面才起作用&#xff0…

【全开源】JAVA上门家政服务系统源码微信小程序+微信公众号+APP+H5

功能介绍 用户端&#xff1a;精准分类、支持家政、维修、万能服务、一口价、报价、线上、各类家政服务、优惠专区、师傅入驻、商家入驻、我的需求、补费明细、我的投诉 师傅端&#xff1a;接单池、消息通知、接单管理、今日订单、师傅入驻、我的钱包、实名认证 商家端&#…

【排序算法】之希尔排序

一、算法介绍 希尔排序(Shell Sort)是插入排序的一种&#xff0c;它是针对直接插入排序算法的改进。希尔排序又称缩小增量排序&#xff0c;因 DL.Shell 于 1959 年提出而得名。它通过比较相距一定间隔的元素来进行&#xff0c;各趟比较所用的距离随着算法的进行而减小&#xf…