【垃圾回收器】基于Go实现引用计数法(ReferenceCount)

文章详细介绍了引用计数法的原理,包括其优点和缺点,特别是对于循环引用的问题。通过一个Go语言实现的例子,展示了如何使用引用计数法管理内存,包括对象的引用更新、计数器的增减以及对象的回收。此外,还涉及到了内存分配和回收的机制,以及如何处理空闲链表来减少内存碎片。

不想传火的,可以点击下面的链接!

github:GCByGO

给我点赞嘛,球球了!
请添加图片描述

What This?

现象

引用计数法是一种垃圾回收算法,用于跟踪对象被引用的次数。在该算法中,每个对象都会维护一个计数器,记录有多少个指针指向它。

当一个新指针引用一个对象时,该对象的计数器就会加1,当指针被释放时,对象的计数器就会减1。

当对象的计数器变为0时,说明该对象没有任何指针指向它,也就意味着该对象可以被回收。

引用计数法会定期扫描内存中的对象,并回收计数器为0的对象。

优缺点

  • 优点(快):引用计数法的优点是回收垃圾的时机可以很快,因为对象的计数器是在实时更新的。
  • 缺点(循环引用):是如果存在循环引用,即两个或多个对象互相引用,但是没有其他对象引用它们,那么这些对象的计数器永远不会变为0,也就无法回收它们,会导致内存泄漏。

因此,引用计数法通常需要与其他垃圾回收算法一起使用,以解决循环引用的问题。

举例

肯定有人没耐心看文字!不过谁叫我人心善呢,见不得你们这么痛苦。

在这里插入图片描述

计数器的构成其实很简单,就是只有计数器和域,

  • 域:专门存放自身节点与其他节点的引用关系,即自己的指针->其他节点
  • 计数器:统计被引用的次数
    在这里插入图片描述

下面的两幅图就很形象了。

一开始我们创建了ABCD这四个节点,其中C被A引用
请添加图片描述
然后修改A->C变成A->B之后的结果如下;
请添加图片描述
你学废了吗?

实现

伪代码

update_ptr() 函数:用于更新指针 ptr,使其指向对象 obj,同时进行计数器值的增减。
注意:

这里的ptr有两重含义,意思就是如上图的修改A->C变成A->B涉及到的两个不同的指针。

另外要保证先增加后减少,这样就可以避免ptrobj是同一个对象的情况所导致的被误删。
在这里插入图片描述
inc_ref_cnt() 函数:计数器增量操作。
在这里插入图片描述
dec_ref_cnt()函数:计数器减量操作。
在这里插入图片描述

GO实现

目录结构

ReferenceCount
	|		|
	|	main.go
	|
	----->PKG
		   |
		   ------>allocate.go
		   ------>createData.go
		   ------>freeLinkedList.go
		   ------>object.go

链表freeLinkedList.go

定义了一个 FreeLinkedList 的数据结构,它是一个基于链表的数据结构,其中的节点被定义为 Node 结构体。链表的头节点是一个指向第一个节点的指针。

Node 结构体包含了一个 data 属性,用于存储节点的数据值,以及一个 next 属性,指向下一个节点。String() 方法实现了将节点的数据值格式化输出的功能。

type Node struct {
   
   
	data interface{
   
   }
	next *Node
}

type FreeLinkedList struct {
   
   
	head *Node
}

这个 FreeLinkedList 数据结构提供了以下几个方法:

  • insertAtEnd:在链表的末尾插入一个新的节点,新节点的数据值是传入的参数。
func (list *FreeLinkedList) insertAtEnd(data interface{
   
   }) {
   
   
	newNode := &Node{
   
   data: data}
	if list.head == nil {
   
   
		list.head = newNode
	} else {
   
   
		current := list.head
		for current.next != nil {
   
   
			current = current.next
		}
		current.next = newNode
	}
}
  • deleteNode:删除链表中的一个节点,被删除节点的数据值是传入的参数。
func (list *FreeLinkedList) deleteNode(data interface{
   
   }) {
   
   
	if list.head == nil {
   
   
		return
	}
	if list.head.data == data {
   
   
		list.head = list.head.next
		return
	}
	prev := list.head
	current := list.head.next
	for current != nil {
   
   
		if current.data == data {
   
   
			prev.next = current.next
			return
		}
		prev = current
		current = current.next
	}
}
  • printLinkedList:打印整个链表,从头节点开始依次输出每个节点的数据值。
func (n *Node) String() string {
   
   
	return fmt.Sprintf("%v", n.data)
}

func (list 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值