jvm垃圾收集器-三色标记算法

1.对象已死吗?

在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”(即不可能再被任何途径使用的对象).

引计数法

引用计数算法是一种垃圾回收算法,它通过记录每个对象被引用的次数来判断对象是否已经成为垃圾并进行回收。当对象被创建时,其引用计数器初始化为0,当有其他对象引用该对象时,其引用计数器就会加1,当引用该对象的对象被销毁时,其引用计数器就会减1。当一个对象的引用计数器变为0时,即表示该对象已经没有被任何其他对象引用,可以被认为是垃圾对象,并被回收。

可达性分析

这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。

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

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象。
  • 方法区中类静态属性引用的对象。
  • 方法区中常量引用的对象。
  • 本地方法栈中JNI(即一般说的Native方法)引用的对象。
2.引用的分类

在JDK 1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4种,这4种引用强度依次逐渐减弱。

  • 强引用就是指在程序代码之中普遍存在的,类似“Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
  • 软引用是用来描述一些还有用但并非必需的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK 1.2之后,提供了SoftReference类来实现软引用。
  • 弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2之后,提供了WeakReference类来实现弱引用。
  • 虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2之后,提供了PhantomReference类来实现虚引用。
3.三色标记算法
3.1三色标记标记流程

我们知道CMS垃圾回收器是通过可达性分析找到存活对象,然后给存活对象打个标记,最终在清理的时候,如果一个对象没有任何标记,就表示这个对象不可达,需要被清理。而标记算法就是使用三色标记算法。一般使用白色、黑色和灰色来表示。

  • 白色:说明这个对象没有被标记过,在初始阶段,所有对象都是白色,整个过程枚举完最后仍是白色的对象会被当做垃圾对象被清理。
  • 灰色:这个对象正在被标记,但是这个对象直接引用的对象中,至少还有一个没有被标记过。
  • 黑色:对象和他直接引用的所有对象都被标记过,对直接引用的对象下一级引用不做要求,比如A只引用了B,B引用了C、D,那么只要A和B都被标记过,A就是黑色,即使B所引用的C或D还没有被访问到,此时B就是灰色。

根据定义jvm中三色标记算法大致流程是:

  1. 首先从GC Roots开始标记,他们所有直接引用对象变成灰色,自己本身变为黑色(GC Roots对象本身不是垃圾),这里我们用队列去存储灰色对象,把这些灰色对象放到队列中。
  2. 然后从队列中取出灰色对象继续进行分析:将这个对象所有直接引用变成灰色,放入队列中,然后这个对象变为黑色;如果没有直接引用就直接变为黑色。
  3. 继续从队列中取出一个灰色对象,重复第二步,一直到灰色队列为空。
  4. 分析完后,仍然是白色的对象,就是不可大对象,可以作为垃圾被回收。
  5. 最后重置标记状态。

下面提供一个例子,加深下印象:

刚开始所有对象都是白色

三色标记初始阶段(标记的初始解读,不是CMS的初始标记),所有GC Roots直接引用(A、B)变成灰色,放入队列中,GC Roots变成了黑色

然后从灰色队列中取出一个灰色对象进行标记,比如A、将他直接引用C、D变成灰色,放入队列,A因为已扫描完它的直接引用对象,所以变成黑色:

继续从队列中取出灰色对象B,但是E没有直接引用其他对象,将B标记为黑色:

根据上述步骤,取出C 、D 对象进行分析,他们都没有直接引用其他对象,那么就变为黑色:

最终分析标记结束后,还有一个E对象是白色,说明此E 对象是一个垃圾对象,不可访问,可以被清理掉。

3.2三色标记的缺陷
  • 多标-浮动垃圾(标记为不是垃圾对象,变成了垃圾)

如果整个标记过程是STW的,那么没有任何问题,但是并发标记过程中,用户线程也在运行,那么对象引用关系很可能发生变化,进而导致前面提到过的两个问题的出现。

比如垃圾回收线程标记回到上面的这个状态:

此时E对象已经被标记为黑色,表示不是垃圾,不会被清除,因为处在并发标记阶段,同一时刻某个用户线将GC Roots和B对象之间的关系断开了(objRoots.b = null;),如图:

很显然,B对象变为了垃圾对象,但是由于之前被标记为黑色,就不会被当作垃圾回收,这种问题称之为浮动垃圾。

浮动垃圾的问题,影响不大,即使本次不清理,下次GC也会被清理,而且在并发清理阶段也会产生所谓的浮动垃圾,因为用户线程也在不断地断开引用,影响不大。

  • 漏标-错杀问题(标记为垃圾对象,变成了非垃圾)

如果一个非垃圾对象,变成了垃圾,后果就比较严重,再回到上面的状态:

这里标记线程执行到分析B对象,但是刚好发生线程切换,操作系统调度用户线程来运行,而用户线程先执行A.f = E;那么引用关系变成了

用户线程做完上述动作,GC线程重新开始运行,按照之前的流程继续走,从对类中取出B对象,发现对象没有直接引用,那么B对象变成了黑色:

接着继续取出 C、D 三个灰色对象,他们没有直接引用,那么变为黑色对象:

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

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

相关文章

挑战30天学完Python:Day16 日期时间

📘 Day 16 🎉 本系列为Python基础学习,原稿来源于 30-Days-Of-Python 英文项目,大奇主要是对其本地化翻译、逐条验证和补充,想通过30天完成正儿八经的系统化实践。此系列适合零基础同学,或仅了解Python一点…

【SQL注入】靶场SQLI DUMB SERIES-24通过二次注入重置用户密码

先使用已知信息admin/admin登录进去查下题,发现可以修改密码 猜测可能存在的SQL语句:UPDATE user SET password新密码 WHERE user用户名 and password旧密码 假设我们知道有个admin用户,但是不知道其密码,如何可以将其密码重置&…

leetcode日记(32)字符串相乘

做了很久很久……真的太繁琐了!! class Solution { public:string multiply(string num1, string num2) {string s;string str;if (num1 "0" || num2 "0") return "0";for(int inum2.size()-1;i>0;i--){int c2num2[…

java效率为什么比c/c++慢,蓝桥杯上java只得50分,c++通过?

java效率为什么比c/c慢,蓝桥杯上java只得50分,c通过? 在开始前我有一些资料,是我根据网友给的问题精心整理了一份「c的资料从专业入门到高级教程」, 点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大…

SpringBoot启动报错:Failed to load property source from ‘file:/D:.....

SpringBoot启动报错:Failed to load property source from file:/D:… SpringBoot启动爆如图的错误 2024-02-22 20:57:42.865 ERROR 23024 --- [ restartedMain] o.s.boot.SpringApplication : Application run failedjava.lang.IllegalStateExce…

力扣102 二叉树的层序遍历 Java版本

文章目录 题目描述思路代码 题目描述 给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:[[3],[…

学习大数据所需的java基础(5)

文章目录 集合框架Collection接口迭代器迭代器基本使用迭代器底层原理并发修改异常 数据结构栈队列数组链表 List接口底层源码分析 LinkList集合LinkedList底层成员解释说明LinkedList中get方法的源码分析LinkedList中add方法的源码分析 增强for增强for的介绍以及基本使用发2.使…

Druid无法登录监控页面

问题表现:在配置和依赖都正确的情况下,无法通过配置的用户名密码登录Druid的监控页面 检查配置发现 配置的用户名和密码和请求中参数是一致的🤔 Debug发现 ResourceServlet 是Druid的登录实现, 且调试发现usernameParam是null&am…

十六、多边形填充和绘制

项目功能实现&#xff1a;对多边形进行轮廓绘制和填充 按照之前的博文结构来&#xff0c;这里就不在赘述了 一、头文件 mult-drawing.h #pragma once#include<opencv2/opencv.hpp>using namespace cv;class Mult_Drawing { public:void mult_drawing(); };#pragma onc…

顺序表经典算法及其相关思考

27. 移除元素 - 力扣&#xff08;LeetCode&#xff09; 思路一 利用顺序表中的SLDestroy函数的思想&#xff0c;遇到等于val值的就挪动 思路二 双指针法&#xff1a;不停的将和val不相等的数字往前放。此时的des更像一个空数组&#xff0c;里面存放的都是和val不相等、能够存…

L2 清点代码库----PTA(疑问)

上图转自新浪微博&#xff1a;“阿里代码库有几亿行代码&#xff0c;但其中有很多功能重复的代码&#xff0c;比如单单快排就被重写了几百遍。请设计一个程序&#xff0c;能够将代码库中所有功能重复的代码找出。各位大佬有啥想法&#xff0c;我当时就懵了&#xff0c;然后就挂…

接口自动化测试丨如何实现多套环境的自动化测试?

在敏捷迭代的项目中&#xff0c;通常会将后台服务部署到多套测试环境。那么在进行接口自动化测试时&#xff0c;则需要将服务器的域名进行配置。使用一套接口测试脚本&#xff0c;通过切换域名地址配置&#xff0c;实现多套环境的自动化测试。 实战练习 分别准备两套测试环境…