JVM之标记算法的详细解析

三色标记
标记算法

三色标记法把遍历对象图过程中遇到的对象,标记成以下三种颜色:

  • 白色:尚未访问过

  • 灰色:本对象已访问过,但是本对象引用到的其他对象尚未全部访问

  • 黑色:本对象已访问过,而且本对象引用到的其他对象也全部访问完成

当 Stop The World (STW) 时,对象间的引用是不会发生变化的,可以轻松完成标记,遍历访问过程为:

  1. 初始时,所有对象都在白色集合

  2. 将 GC Roots 直接引用到的对象挪到灰色集合

  3. 从灰色集合中获取对象:

    • 将本对象引用到的其他对象全部挪到灰色集合中

    • 将本对象挪到黑色集合里面

  4. 重复步骤 3,直至灰色集合为空时结束

  5. 结束后,仍在白色集合的对象即为 GC Roots 不可达,可以进行回收

并发标记

并发标记时,对象间的引用可能发生变化,多标和漏标的情况就有可能发生

多标情况:当 E 变为灰色或黑色时,其他线程断开的 D 对 E 的引用,导致这部分对象仍会被标记为存活,本轮 GC 不会回收这部分内存,这部分本应该回收但是没有回收到的内存,被称之为浮动垃圾

  • 针对并发标记开始后的新对象,通常的做法是直接全部当成黑色,也算浮动垃圾

  • 浮动垃圾并不会影响应用程序的正确性,只是需要等到下一轮垃圾回收中才被清除

漏标情况:

  • 条件一:灰色对象断开了对一个白色对象的引用(直接或间接),即灰色对象原成员变量的引用发生了变化

  • 条件二:其他线程中修改了黑色对象,插入了一条或多条对该白色对象的新引用

  • 结果:导致该白色对象当作垃圾被 GC,影响到了程序的正确性

代码角度解释漏标:

Object G = objE.fieldG; // 读
objE.fieldG = null;     // 写
objD.fieldG = G;        // 写

为了解决问题,可以操作上面三步,将对象 G 记录起来,然后作为灰色对象再进行遍历,比如放到一个特定的集合,等初始的 GC Roots 遍历完(并发标记),再遍历该集合(重新标记)

所以重新标记需要 STW,应用程序一直在运行,该集合可能会一直增加新的对象,导致永远都运行不完

解决方法:添加读写屏障,读屏障拦截第一步,写屏障拦截第二三步,在读写前后进行一些后置处理:

  • 写屏障 + 增量更新:黑色对象新增引用,会将黑色对象变成灰色对象,最后对该节点重新扫描

    增量更新 (Incremental Update) 破坏了条件二,从而保证了不会漏标

    缺点:对黑色变灰的对象重新扫描所有引用,比较耗费时间

  • 写屏障 (Store Barrier) + SATB:当原来成员变量的引用发生变化之前,记录下原来的引用对象

    保留 GC 开始时的对象图,即原始快照 SATB,当 GC Roots 确定后,对象图就已经确定,那后续的标记也应该是按照这个时刻的对象图走,如果期间对白色对象有了新的引用会记录下来,并且将白色对象变灰(说明可达了,并且原始快照中本来就应该是灰色对象),最后重新扫描该对象的引用关系

    SATB (Snapshot At The Beginning) 破坏了条件一,从而保证了不会漏标

  • 读屏障 (Load Barrier):破坏条件二,黑色对象引用白色对象的前提是获取到该对象,此时读屏障发挥作用

以 Java HotSpot VM 为例,其并发标记时对漏标的处理方案如下:

  • CMS:写屏障 + 增量更新

  • G1:写屏障 + SATB

  • ZGC:读屏障

finalization

Java 语言提供了对象终止(finalization)机制来允许开发人员提供对象被销毁之前的自定义处理逻辑

垃圾回收此对象之前,会先调用这个对象的 finalize() 方法,finalize() 方法允许在子类中被重写,用于在对象被回收时进行后置处理,通常在这个方法中进行一些资源释放和清理,比如关闭文件、套接字和数据库连接等

生存 OR 死亡:如果从所有的根节点都无法访问到某个对象,说明对象己经不再使用,此对象需要被回收。但事实上这时候它们暂时处于缓刑阶段。一个无法触及的对象有可能在某个条件下复活自己,所以虚拟机中的对象可能的三种状态:

  • 可触及的:从根节点开始,可以到达这个对象

  • 可复活的:对象的所有引用都被释放,但是对象有可能在 finalize() 中复活

  • 不可触及的:对象的 finalize() 被调用并且没有复活,那么就会进入不可触及状态,不可触及的对象不可能被复活,因为 finalize() 只会被调用一次,等到这个对象再被标记为可回收时就必须回收

永远不要主动调用某个对象的 finalize() 方法,应该交给垃圾回收机制调用,原因:

  • finalize() 时可能会导致对象复活

  • finalize() 方法的执行时间是没有保障的,完全由 GC 线程决定,极端情况下,若不发生 GC,则 finalize() 方法将没有执行机会,因为优先级比较低,即使主动调用该方法,也不会因此就直接进行回收

  • 一个糟糕的 finalize() 会严重影响 GC 的性能

引用分析

无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否可达,判定对象是否可被回收都与引用有关,Java 提供了四种强度不同的引用类型

  1. 强引用:被强引用关联的对象不会被回收,只有所有 GCRoots 都不通过强引用引用该对象,才能被垃圾回收

    • 强引用可以直接访问目标对象

    • 虚拟机宁愿抛出 OOM 异常,也不会回收强引用所指向对象

    • 强引用可能导致内存泄漏

    Object obj = new Object();//使用 new 一个新对象的方式来创建强引用

  2. 软引用(SoftReference):被软引用关联的对象只有在内存不够的情况下才会被回收

    • 仅(可能有强引用,一个对象可以被多个引用)有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象

    • 配合引用队列来释放软引用自身,在构造软引用时,可以指定一个引用队列,当软引用对象被回收时,就会加入指定的引用队列,通过这个队列可以跟踪对象的回收情况

    • 软引用通常用来实现内存敏感的缓存,比如高速缓存就有用到软引用;如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时不会耗尽内存

    Object obj = new Object();
    SoftReference<Object> sf = new SoftReference<Object>(obj);
    obj = null;  // 使对象只被软引用关联

  3. 弱引用(WeakReference):被弱引用关联的对象一定会被回收,只能存活到下一次垃圾回收发生之前

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

    • 配合引用队列来释放弱引用自身

    • WeakHashMap 用来存储图片信息,可以在内存不足的时候及时回收,避免了 OOM

    Object obj = new Object();
    WeakReference<Object> wf = new WeakReference<Object>(obj);
    obj = null;

  4. 虚引用(PhantomReference):也称为幽灵引用或者幻影引用,是所有引用类型中最弱的一个

    ThreadLocal 内部是虚引用

    • 一个对象是否有虚引用的存在,不会对其生存时间造成影响,也无法通过虚引用得到一个对象

    • 为对象设置虚引用的唯一目的是在于跟踪垃圾回收过程,能在这个对象被回收时收到一个系统通知

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

    Object obj = new Object();
    PhantomReference<Object> pf = new PhantomReference<Object>(obj, null);
    obj = null;
  5. 终结器引用(finalization)

    • 无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize 方法,第二次 GC 时才能回收被引用对象

无用属性
无用类

方法区主要回收的是无用的类

判定一个类是否是无用的类,需要同时满足下面 3 个条件:

  • 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例

  • 加载该类的 ClassLoader 已经被回收

  • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法

虚拟机可以对满足上述 3 个条件的无用类进行回收,这里说的仅仅是可以,而并不是和对象一样不使用了就会必然被回收

废弃常量

在常量池中存在字符串 "abc",如果当前没有任何 String 对象引用该常量,说明常量 "abc" 是废弃常量,如果这时发生内存回收的话而且有必要的话(内存不够用),"abc" 就会被系统清理出常量池

静态变量

类加载时(第一次访问),这个类中所有静态成员就会被加载到静态变量区,该区域的成员一旦创建,直到程序退出才会被回收

如果是静态引用类型的变量,静态变量区只存储一份对象的引用地址,真正的对象在堆内,如果要回收该对象可以设置引用为 null

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

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

相关文章

DiffusionDet:用于物体检测的扩散模型

论文标题&#xff1a;DiffusionDet: Diffusion Model for Object Detection 论文地址&#xff1a;https://arxiv.org/pdf/2211.09788 DiffusionDet&#xff1a;用于物体检测的扩散模型 检测模型发展物体检测 DiffusionDet方法预备知识DiffusionDet前向扩散过程DiffusionDet反向…

【gtest】 C++ 的测试框架之使用 gtest 编写单元测试

目录 &#x1f30a;前言 &#x1f30a;使用 cmake 启动并运行 gtest &#x1f30d;1. 设置项目 &#x1f30d;2. 创建并运行二进制文件 &#x1f30a;1. gtest 入门 &#x1f30d;1.1 断言&#xff08;assertions&#xff09; &#x1f30d;1.2 简单测试 &#x1f30d;…

最新(2024年)安装gdal库方法

方法一&#xff1a;直接 conda install gdal 方法二&#xff1a;https://github.com/cgohlke/geospatial-wheels/releases/tag/v2024.2.18 这个链接里下载whl文件

Day51 代码随想录打卡|二叉树篇---二叉搜索树的最小绝对差

题目&#xff08;leecode T530&#xff09;&#xff1a; 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&#xff0c;其数值等于两值之差的绝对值。 方法&#xff1a;本题计算二叉搜索树的最小绝对差&#xff0c;涉…

快慢指针在字符串中的应用-443. 压缩字符串

题目链接及描述 443. 压缩字符串 - 力扣&#xff08;LeetCode&#xff09; 题目分析 这个题目总体不算太难&#xff0c;如果之前接触过双指针&#xff08;快慢指针&#xff09;的话&#xff0c;比较好做。题目可以理解为计算数组中对应各个连续字符出现的次数&#xff0c;并将…

测试基础13:测试用例设计方法-错误推断、因果图判定表

课程大纲 1、错误推测法 靠主观经验和直觉来推测可能容易出现问题的功能或场景&#xff0c;设计相关测试用例进行验证。 2、因果图&判定表 2.1定义 因果图和判定表是分析和表达多逻辑条件下&#xff0c;执行不同操作的情况的工具。 &#xff08;因果图和判定表配合使用&a…

生产运作管理--第六版陈荣秋

第一章&#xff1a; 生产运作的分类有哪些&#xff1f; 答&#xff1a;可以分为两大类&#xff1a; 产品生产: 产品生产是通过物理或者化学作用将有形输入转化为有形输出的过程。 按照工艺过程的特点&#xff0c;可以分为&#xff1a; 连续性生产&#xff1a;物料均匀、连续的按…

TCP/IP协议,三次握手,四次挥手

IP - 网际协议 IP 负责计算机之间的通信。 IP 负责在因特网上发送和接收数据包。 HTTP - 超文本传输协议 HTTP 负责 web 服务器与 web 浏览器之间的通信。 HTTP 用于从 web 客户端&#xff08;浏览器&#xff09;向 web 服务器发送请求&#xff0c;并从 web 服务器向 web …

TCP三次握手和四次挥手过程简介(抓包分析,简单易懂,小白)

接上篇 传输层部分 链路层、网络层、传输层和应用层协议详解分析-CSDN博客文章浏览阅读689次&#xff0c;点赞10次&#xff0c;收藏15次。wireshark抓包分析-CSDN博客wireshark是网络包分析工具网络包分析工具的主要作用是尝试捕获网络包&#xff0c;并尝试显示包的尽可能详细…

6年前端社招一个月上岸20K经历,附简历

面经哥只做互联网社招面试经历分享&#xff0c;关注我&#xff0c;每日推送精选面经&#xff0c;面试前&#xff0c;先找面经哥 本人普通本科6年前端经历&#xff0c;从年前开始准备跳槽&#xff0c;一开始感觉自己履历算不上突出&#xff0c;经历过迷茫和沮丧的时候&#xff0…

【计算机毕业设计】基于Springboot的B2B平台医疗病历交互系统【源码+lw+部署文档】

包含论文源码的压缩包较大&#xff0c;请私信或者加我的绿色小软件获取 免责声明&#xff1a;资料部分来源于合法的互联网渠道收集和整理&#xff0c;部分自己学习积累成果&#xff0c;供大家学习参考与交流。收取的费用仅用于收集和整理资料耗费时间的酬劳。 本人尊重原创作者…

MySQL日志(二):MySQL抖动

一条SQL语句&#xff0c; 正常执行的时候特别快&#xff0c; 但是有时也不知道怎么回事&#xff0c; 它就会变得特别慢&#xff0c; 并且这样的场景很难复现&#xff0c; 它不只随机&#xff0c; 而且持续时间还很短。 看上去&#xff0c; 这就像是数据库“抖”了一下。 今天&…

redis 笔记2之哨兵

文章目录 一、哨兵1.1 简介1.2 实操1.2.1 sentinel.conf1.2.2 问题1.2.3 哨兵执行流程和选举原理1.2.4 使用建议 一、哨兵 1.1 简介 上篇说了复制&#xff0c;有个缺点就是主机宕机之后&#xff0c;从机只会原地待命&#xff0c;并不能升级为主机&#xff0c;这就不能保证对外…

Linux中Web服务器配置和管理(Apache)

文章目录 一、WEB服务器介绍1.1、WEB服务器概述1.2、WEB服务器的发展历史1.3、WEB服务器的优点与缺点1.4、WEB服务器的工作流程 二、Apache介绍2.1、Apache是什么2.2、Apache的发展史与应用场景2.3、Apache的特点2.4、Apache的工作原理2.5、Apache的模块 三、安装使用Apache服务…

Apache druid未授权命令执行漏洞复现

简介 Apache Druid是一个实时分析型数据库&#xff0c;旨在对大型数据集进行快速的查询分析&#xff08;"OLAP"查询)。Druid最常被当做数据库来用以支持实时摄取、高性能查询和高稳定运行的应用场景&#xff0c;同时&#xff0c;Druid也通常被用来助力分析型应用的图…

牛客仓鼠的鸡蛋

分析一下判断语句 如果能放就输出位置 不能放就输出-1 不能放的条件是最大值小于要放的鸡蛋数量&#xff0c;线段树维护最大值 放的位置用线段树二分 每个篮子不能放超过k堆鸡蛋&#xff0c;记录一下每个篮子放的次数&#xff0c;次数等于k后给最大值附上0即可 // Proble…

使用powershell筛选AD域控不能自主更改的用户并变更

# 查询“用户不能更改密码”为勾选状态的所有域用户&#xff0c;将域账户、姓名、勾选状态作为结果保存到C:\result\result.csvGet-ADUser -Filter * -Properties CannotChangePassword | Where-Object { $_.CannotChangePassword -eq $true } | Select SamAccountName, Name, …

javaWeb项目-ssm+vue在线购物系统功能介绍

本项目源码&#xff1a;java-ssmvue在线购物系统的设计与实现源码说明文档资料资源-CSDN文库 项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;sprin…

UnityAPI学习之延时调用(Invoke)

延时调用&#xff08;Invoke&#xff09; 当我们进行简单函数的延时调用不想使用协程时&#xff0c;我们可以使用Invoke()函数 using System.Collections; using System.Collections.Generic; using UnityEngine;public class NO15_Invoke : MonoBehaviour {//显示在每次生成…

MySQL 日志(一)

本篇主要介绍MySQL日志的相关内容。 目录 一、日志简介 常用日志 一般查询日志和慢查询日志的输出形式 日志表 二、一般查询日志 三、慢查询日志 四、错误日志 一、日志简介 常用日志 在MySQL中常用的日志主要有如下几种&#xff1a; 这些日志通常情况下都是关闭的&a…