【JVM】垃圾回收机制(GC)之引用计数和可达性分析

devtools/2024/10/4 5:25:56/

1. 引用计数

这种思想方法,并没有在 JVM 中使用,但是广泛应用于其他主流语言的垃圾回收机制中(PythonPHP)。

《深入理解 Java 虚拟机》中谈到了引用计数,就导致有些面试官还是会问

给每个对象安排一个额外的空间,空间里要保存当前这个对象有几个引用

Test a = new Test();
Test b = a;
a = null;
b = null;

image.png|523

  • new 出对象的时候,就在堆上开辟了一块空间,并且在前面额外有一块空间用来存储引用计数
  • 当把对象的地址给到栈上的局部变量的时候,这个引用就指向了这个对象,引用计数就变成了 1
  • 当引用 b 同样指向这个对象的时候,引用计数就变成了 2
  • 当引用 a 的值由对象的地址变为 null 的时候,a 引用就销毁了,引用计数变为 1
  • 当引用 b 的值由对象的地址变为 null 的时候,b 引用也销毁了,引用计数变为 0

此时垃圾回收机制发现对象的引用计数为 0,说明这个对象就可以释放掉了

  • 引用计数为 0,就说明这个对象是垃圾了
  • 有专门的线程,去获取到当前每个对象的引用计数的情况

存在问题

引用计数机制,是一个简单有效的机制,存在两个关键问题

1. 消耗额外的内存空间

要给每个对象都安排一个计数器,就算计数器按照两个字节算,整个程序中对象数目很多,总的消耗空间也会非常多;尤其是如果每个对象体积比较小,假设每个对象四个字节,计数器消耗的空间,就达到了对象空间的一半

类似于花钱买 100 平的房子,实际上你房子的使用面积也就 70 多平(非常难受)

2. “循环引用“问题

引用计数可能会产生“循环引用的问题”。此时,引用计数就无法正确工作了

class Test {Test t;
}Test a = new Test();
Test b = new Test();a.t = b;
b.t = a;a = null;
b = null;

image.png

  • Test 对象里面有一个成员变量 t,他的类型也是 Test,也就是说它也可以引用一个对象
  • a.t = b 的意思是:将 a 引用对象中的 t 成员变量的值赋为 b 的引用
    • 所以此时第二个引用对象就会有两个引用指向,一个是 a,一个是 a.t
    • 所以第二个引用对象的引用计数就会变成 2
  • 同理,b.t=a 的结果就是第一个引用计数也会变成 2

  • ab 都被赋值为 0 之后,两个对象的引用计数都变成了 1,但此时这两个对象都没法使用了(双方的引用指向都在对方那里,类似于“死锁”的情况)。由于引用计数不为 0,也没法被回收

2. 可达性分析(JVM 用的)

本质上是用“时间换空间”,相比于引用计数,需要小号更多的额外的时间。但是总体来说还是可控的,不会产生类似于“循环引用”这样的问题

在写代码的过程中,会定义很多的变量。比如,栈上的局部变量/方法区中的静态类型的变量/常量池引用的对象…

  • 就可以从这些变量作为起点出发,尝去进行“遍历”。
  • 所谓遍历就是会沿着这些变量中持有的引用类型的成员,再进一步的往下进行访问
  • 所有能被访问到的对象,自然就不是垃圾,剩下的遍历一圈也访问不到的对象,自然就是垃圾了

比如有如下代码:

class Node {char val;Node left;Node right;
}Node buildTree() {Node a = new Node();Node b = new Node();Node c = new Node();Node d = new Node();Node e = new Node();Node f = new Node();Node g = new Node();a.right = b;a.left = c;b.left = d;b.right = e;e.left = g;c.right = f;return a;
)Node root = buildTree();

image.png|369
虽然这个代码中,只有一个 root 这样的引用,但是实际上上述 7 个节点对象都是“可达”的

  • b == root. left;
  • c == root. right;
  • d == root. left. left;
  • 依此类推,上述的对象都能通过 . 的方式访问到
    JVM 中存在扫描线程,会不停地尝试对代码中已有的这些变量进行遍历,尽可能多的访问到对象

上述代码中,如果执行这个代码:root.right.right = null;

  • 就会导致 cf 之间断开了,此时 f 这个对象就被“孤立”了
  • 按照上述从 root 出发进行遍历的操作就也无法访问到 f 了,f 这个节点对象就称为“不可达image.png|283
  • 如果 ac 之间断开了,此时 c 就不可达了。由于访问 f 必须通过 cc 不可达就导致 f 不可达。所以此时 cf 都是垃圾了
  • 如果 root=null,此时整棵树都是垃圾了

JVM 自身知道一共有哪些对象,通过可达性分析的遍历,把可达的对象都标记出来了,剩下的自然就是不可达的了


http://www.ppmy.cn/devtools/120472.html

相关文章

誉天Linux云计算课程学什么?为什么保障就业?

一个IT工程师相当于干了哪些职业? 其中置顶回答生动而形象地描绘道: 一个IT工程师宛如一个超级多面手,相当于——加班狂程序员测试工程师实施工程师网络工程师电工装卸工搬运工超人。 此中酸甜苦辣咸,相信很多小伙伴们都深有体会。除了典…

Unity3D播放GIF图片使用Animation来制作动画

系列文章目录 unity工具 文章目录 系列文章目录👉前言👉一、下载GIF动图,用PS制作导出帧动画图片👉二、使用Animation制作动画👉三、脚本控制动画播放👉壁纸分享👉总结👉前言 unity播放gif图片,本身是不支持的,但是可以使用其他方法来实现, 1.有一种使用System…

IP与网关的关系

在计算机网络中,IP地址和网关是两个重要的概念,它们在网络通信中起着关键作用。理解这两者之间的关系,有助于更好地管理和配置网络。 什么是IP地址? IP地址(Internet Protocol Address)是分配给网络中每个…

城市空间设计对居民生活质量的影响:构建宜居城市的蓝图

在快节奏的现代生活中,城市不仅是经济活动的中心,更是人们生活、工作、休闲的综合载体。本文旨在深入探讨城市空间设计如何通过科学规划、人性化考量以及生态融合,为居民打造更加宜居、和谐的生活环境。 1. 促进社区互动与归属感 城市空间设…

RabbitMQ的高级特性-TTL

TTL(Time to Live, 过期时间), 即过期时间. RabbitMQ可以对消息和队列设置TTL.当消息到达存活时间之后, 还没有被消费, 就会被⾃动清除 设置消息的TTL: ①设置队列的TTL,队列中所有的消息都有相同的过期时间 …

Unity3D入门(四) : Android和Unity3D交互 - Unity调用Android

1. 前言 上篇文章,我们讲了如何通过Android调用Unity3D。这篇文章,我们来讲一下Unity3D怎么调用Android。 1.1 unity和Android的三种通信方式 Unity官方提供的接口 : 有一个弊端,它有一个传输内容量的一个限制,传输内容过大或过…

建投数据自主研发相关系统获得欧拉操作系统及华为鲲鹏技术认证书

近日,经欧拉生态创新中心和华为技术有限公司测评,建投数据自主研发的投资项目管理系统、全面风险管理信息系统、商业不动产业务系统,完成了基于欧拉操作系统openEuler 22.03、华为鲲鹏Kunpeng 920(Taisha 200)的兼容性…

Python in Excel作图分析实战!

Excel 中的 Python 现已正式发布,适用于 Microsoft 365 商业版和企业版的 Windows 用户。去年 8 月,微软与 Anaconda 合作,通过集成 Python 为 Excel 引入了一个令人兴奋的新增功能,从而可以将 Python 和 Excel 分析无缝结合到同一…