深入学习 JVM 算法 - 引用计数法

举报
激流丶 发表于 2023/06/29 21:06:38 2023/06/29
【摘要】 深入学习 JVM 算法 - 引用计数法

博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家✌

Java知识图谱点击链接:体系化学习Java(Java面试专题)

💕💕 感兴趣的同学可以收藏关注下不然下次找不到哟💕💕

image.png

1、什么是引用计数法

image.png

引用计数法(Reference Counting)是一种内存管理技术,用于跟踪对象的引用数量。在引用计数法中,每个对象都有一个引用计数器,记录着指向该对象的引用数量。当引用计数器为零时,表示没有任何引用指向该对象,该对象可以被释放,回收其占用的内存。

当一个对象被引用时,引用计数器加一;当一个引用被释放时,引用计数器减一。通过不断地增减引用计数器,系统可以动态地追踪对象的引用情况,并在适当的时候回收不再被引用的对象。

引用计数法的优点是实时性好,当没有引用指向一个对象时,该对象可以立即被回收,释放内存资源。然而,引用计数法也存在一些问题,比如循环引用的情况下,对象之间的引用计数可能永远不会为零,导致内存泄漏的发生。为了解决这个问题,通常需要引入其他的内存管理技术,如垃圾回收机制。

2、引用计数法的优缺点

引用计数法的优点包括:

  1. 实时性好:当没有引用指向一个对象时,该对象可以立即被回收,释放内存资源。
  2. 简单高效:引用计数法是一种相对简单的内存管理技术,实现起来较为高效。
  3. 没有必要沿指针查找:引用计数法和 GC 标记 - 清除算法不一样,没必要由根沿指针查找。

引用计数法的缺点包括:

  1. 循环引用问题:当存在循环引用的情况下,对象之间的引用计数可能永远不会为零,导致内存泄漏的发生。
  2. 额外开销:每个对象都需要维护一个引用计数器,这会带来一定的额外开销。
  3. 不支持并发:引用计数法在多线程环境下需要进行额外的同步操作,以确保引用计数的准确性,这可能导致一定的性能损失。
  4. 实现烦琐复杂:引用计数的算法本身很简单,但事实上实现起来却不容易。

3、延迟引用计数法

3.1、什么是延迟引用计数法

延迟引用计数法(Deferred Reference Counting)是一种改进的引用计数法,旨在解决循环引用带来的内存泄漏问题。在延迟引用计数法中,引用计数器不仅仅记录引用的数量,还记录了被引用对象的可能的循环引用路径。

延迟引用计数法的工作原理如下:

  1. 每个对象都有一个引用计数器,记录指向该对象的引用数量。
  2. 当一个对象被引用时,引用计数器加一。
  3. 当一个引用被释放时,引用计数器减一。
  4. 引用计数器减为零时,对象不会立即被释放,而是进入一个延迟释放队列。
  5. 延迟释放队列会定期进行检查,检查对象是否存在循环引用。
  6. 如果发现对象存在循环引用,则将其从延迟释放队列中移除,保留对象的引用。
  7. 如果对象不存在循环引用,则对象最终会被释放,回收其占用的内存。

延迟引用计数法通过延迟释放对象,允许系统在一定时间内检测和解决循环引用问题,从而避免内存泄漏。这种方法的缺点是增加了内存管理的复杂性和延迟释放的开销,但相对于传统的引用计数法,它提供了更好的内存回收机制。

我们在延迟引用计数法中使用 ZCT(Zero Count Table)。ZCT 是一个表,它会事先记录下计数器值在 dec_ref_cnt() 函数的作用下变为 0 的对象。

因为计数器值为 0 的对象不一定都是垃圾,所以暂时先将这些对象保留。
image.png

3.2、dec_ref_cnt() 函数

在延迟引用计数法中用到的 dec_ref_cnt() 函数

dec_ref_cnt(obj){
	 obj.ref_cnt--
	 if(obj.ref_cnt == 0)
		 if(is_full($zct) == TRUE)
			 scan_zct()
			 push($zct, obj)
}

dec_ref_cnt() 函数会接收一个对象作为参数,然后将该对象的引用计数器减一。如果引用计数器减为零,说明没有任何引用指向该对象,可以释放对象所占用的内存资源。

用于记录引用计数。函数首先将 ref_count 减一,然后检查是否减为零。如果是,则调用 release_memory() 函数释放对象的内存。

3.3、延迟引用计数法的优缺点

延迟引用计数法的优点包括:

  1. 解决循环引用问题:延迟引用计数法通过延迟释放对象,允许系统在一定时间内检测和解决循环引用问题,避免了内存泄漏。
  2. 实时性好:当没有引用指向一个对象时,该对象可以立即被回收,释放内存资源。
  3. 相对简单高效:延迟引用计数法相对于其他内存管理技术来说实现起来较为简单,并且在大多数情况下具有较高的性能。

延迟引用计数法的缺点包括:

  1. 额外开销:延迟引用计数法需要维护额外的数据结构来检测循环引用,这会带来一定的额外开销。
  2. 延迟释放的开销:延迟引用计数法需要定期检查和解决循环引用,这可能导致一定的性能损失。
  3. 不支持并发:在多线程环境下,延迟引用计数法需要进行额外的同步操作,以确保引用计数和循环引用检测的准确性,可能导致性能下降。

4、Sticky 引用计数法

4.1、什么是 Sticky 引用计数法

image.png

Sticky 引用计数法是一种内存管理技术,用于解决循环引用问题。它是引用计数法的一种变体,通过引入"sticky"标记来延迟释放对象。

在 Sticky 引用计数法中,除了常规的引用计数器外,还为每个对象引入一个"sticky"标记。当一个对象被引用时,它的引用计数会增加,并且"sticky"标记会被设置为真。当一个对象的引用计数减少到零时,它不会立即被释放,而是进入"sticky"状态。在这个状态下,对象的引用计数不再增加,但它的"sticky"标记仍然为真。

当对象处于"sticky"状态时,系统会定期进行垃圾回收。垃圾回收器会遍历所有的对象,并检查它们的引用计数和"sticky"标记。如果一个对象的引用计数为零且"sticky"标记为真,那么它可以被释放。

Sticky 引用计数法的优点是能够解决循环引用问题,避免内存泄漏。它相对于传统的引用计数法增加了"sticky"标记和垃圾回收的机制,但相比其他内存管理技术,它的实现相对简单。然而,它也存在一些缺点,如引入了额外的开销和延迟释放的性能损失。

4.2、Sticky 引用计数法解决“爆表”有什么办法解决

Sticky 引用计数法可以通过以下两种方式来解决“爆表”(count overflow)的问题:

  1. 无符号整数扩展:在传统的引用计数法中,引用计数器通常使用有符号整数来表示引用计数。当引用计数增加到最大值时,继续增加会导致计数器溢出,变为负数。为了解决这个问题,可以将引用计数器改为无符号整数,这样当计数器达到最大值时,会从最小值重新开始,避免了溢出问题。

  2. Big Counters:另一种解决方案是使用大型计数器。传统的引用计数法通常使用一个字节或几个字节来表示引用计数,这限制了计数器的最大值。为了解决这个问题,可以使用更大的计数器,比如使用64位整数来表示引用计数。这样可以大大增加计数器的最大值,减少计数器溢出的可能性。

5、1 位引用计数法

5.1、什么是 1 位引用计数法

image.png

1 位引用计数法是一种简单的引用计数算法,用于跟踪对象的引用数量。它使用一个单独的位来表示引用计数,只能存储0或1两个值。

在1位引用计数法中,每个对象都有一个与之关联的引用计数位。当一个对象被引用时,引用计数位被设置为1,表示有一个引用指向该对象。当引用被释放时,引用计数位被设置为0,表示没有引用指向该对象。

这种引用计数法的优点是简单、高效。由于只使用一个位来表示引用计数,它占用的内存非常小。而且,当引用计数位为0时,可以立即确定对象没有被引用,可以立即进行垃圾回收。

然而,1位引用计数法也有一些限制。由于只有一个位来表示引用计数,它只能跟踪两种状态(0或1),无法处理多个引用的情况。当多个引用指向同一个对象时,无法准确跟踪引用计数,可能导致引用计数的不准确和错误的内存管理。

5.2、1 位引用计数法的优缺点

1 位引用计数法的优点:

  1. 简单高效:1 位引用计数法只使用一个位来表示引用计数,因此占用的内存非常小,计数操作也非常快速。
  2. 实时垃圾回收:当引用计数位为0时,可以立即确定对象没有被引用,可以立即进行垃圾回收,减少内存占用。

1 位引用计数法的缺点:

  1. 无法处理循环引用:1 位引用计数法无法处理循环引用的情况,即多个对象相互引用导致引用计数无法准确归零,可能导致内存泄漏。
  2. 并发性问题:在多线程环境下,1 位引用计数法可能存在并发性问题,需要额外的同步机制来确保引用计数的准确性。
  3. 频繁的计数操作:由于每次引用的增加和减少都需要修改引用计数位,频繁的计数操作可能会带来一定的性能开销。

5.3、1 位引用计数法解决什么问题

1 位引用计数法主要用于解决内存管理中的垃圾回收问题。它通过跟踪对象的引用数量来确定何时可以安全地释放对象所占用的内存空间。

具体而言,1 位引用计数法可以解决以下问题:

  1. 确定对象是否被引用:通过引用计数位的值,可以确定对象当前是否有引用指向它。当引用计数位为0时,表示对象没有被引用,可以安全地进行垃圾回收。
  2. 实时的垃圾回收:当引用计数位为0时,可以立即进行垃圾回收,释放对象所占用的内存空间。这可以避免内存的过度占用和浪费。
  3. 简单高效:1 位引用计数法只使用一个位来表示引用计数,因此占用的内存非常小,计数操作也非常快速。

然而,需要注意的是,1 位引用计数法无法解决循环引用的问题。当多个对象相互引用形成循环时,引用计数无法准确归零,可能导致内存泄漏。因此,在处理循环引用的情况下,需要使用其他的垃圾回收算法,如标记-清除算法或引用计数算法的改进版本。

6、部分标记- 清除算法

6.1、什么是部分标记- 清除算法

在部分标记 - 清除算法中,对象会被涂成 4 种不同的颜色来进行管理。每个颜色的含义如下所示。

  1. 黑(BLACK):绝对不是垃圾的对象(对象产生时的初始颜色)
  2. 白(WHITE):绝对是垃圾的对象
  3. 灰(GRAY):搜索完毕的对象
  4. 阴影(HATCH):可能是循环垃圾的对象

话虽这么说,事实上并没办法去给对象涂颜色,而是往头中分配 2 位空间,然后用 00~11 的值对应这 4 个颜色,以示区分。本书中用 obj.color 来表示对象 obj 的颜色。obj.color 取 BLACK、WHITE、GRAY、HATCH 中的任意一个值。
image.png

image.png

💕💕 本文由激流原创,原创不易,感谢支持
💕💕喜欢的话记得点赞收藏啊

image.png

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。