引用计数算法

  • 导语
  • 什么是引用计数算法
  • 两种实现方式
  • 算法优点
  • 算法劣势


导语

引用计数算法作为垃圾收集器最早的算法,只需要一个简单的递归就可以实现,有一定优势,也有一定的劣势。JVM不再采用引用计数算法进行垃圾回收,但是这种算法并未被淘汰,在著名的Redis中依然使用。

什么是引用计数算法

对于创建的每一个对象都有一个相关联的计数器,这个计数器记录着该对象被引用的次数,垃圾收集器进行垃圾回收时,对扫描到的每个对象都要判断下该对象计数器是否为0,若为0就会释放该对象的内存空间。这里要注意:遍历该对象属性引用相应的计数都要减一。

伪代码实现

new():分配内存
	ref <- allocate()
	if ref == null
		error "out of memory"
	rc(ref) <- 0 //将ref的引用计数(reference counting) 设置为0
	return ref

atomic write(dest,ref) //更新对象的引用
	addReference(ref)
	deleteReference(dest)
	dest <- ref

addReference(ref):
	if ref != null 
		rc(ref) <- rc(ref)+1
		
deleteReference(ref):
	if ref != null
		rc(ref) <- rc(ref)-1
		if rc(ref) == 0 //如果当前ref的应用计数为0,则表明其将被回收
			for each fld in Pointers(ref)
				deleteReference(*fld)
			free(ref) //释放ref的内存空间

Java代码示例

String p = new String("abc")
String q = new String("def")
p = q

当我们执行代码p=q时,实际上相当于调用了伪代码中的write(p,q)方法,即对p原先指向的对象要进行deleteReference()操作-引用计数减一,原因是p变量不在指向该对象。对于q引用的对象要进行addReference()操作-引用计数加一。

两种实现方式

侵入式与非侵入式,引用计数算法的垃圾收集一般分为侵入式和非侵入式。侵入式的实现方式是将引用计数器直接根植在对象内部,用C++的思想进行解释是,在对象的构造或者拷贝构造中进行加一操作,在对象的析构中进行减一操作,非侵入式思想就是有一块单独的内存空间用于对象的引用计数器。

算法优点

使用引用计数器,内存回收可以穿插在程序中运行,在程序运行中,但发现某一对象的引用计数器为0时,可以立即对该对象所占的内存空间进行回收,这种方式可以避免FULL GC带来的程序暂停。

算法劣势

采用引用计数算法进行垃圾回收,最大的缺点是不能解决循环引用的问题。例如一个父对象持有一个子对象的引用,子对象也持有父对象的引用,这种情况下父子对象将会一直存储在JVM堆中,无法进行回收。

ios 引用计数存在什么地方_内存空间