算法原理

引用计数算法很简单,它实际上是通过在对象头中分配一个空间来保存该对象被引用的次数。如果该对象被其它对象引用,则它的引用计数加一,如果删除对该对象的引用,那么它的引用计数就减一,当该对象的引用计数为0时,那么该对象就会被回收。




比如说,当我们编写以下代码时,

String p = new String("abc")

abc这个字符串对象的引用计数值为1.


引用计数_引用计数

 


而当我们去除abc字符串对象的引用时,则abc字符串对象的引用计数减1

p = null


引用计数_内存泄漏_02




由此可见,当对象的引用计数为0时,垃圾回收就发生了。引用计数垃圾收集机制,它是在引用计数变化为0时即刻发生,而且只针对某一个对象以及它所依赖的其它对象。所以,我们一般也称呼引用计数垃圾收集为直接的垃圾收集机制,

因此,采用引用计数的垃圾收集不属于严格意义上的"Stop-The-World"的垃圾收集机制。



当我们执行代码p=q时, 即对p原先指向的对象要进行deleteReference()操作 - 引用计数减一,因为p变量不再指向该对象了,而对q原先指向的对象要进行addReference()操作 - 引用计数加一。


引用计数_数据_03

 


第二点需要理解的是,当某个对象的引用计数减为0时,collector需要递归遍历它所指向的所有域,将它所有域所指向的对象的引用计数都减一,然后才能回收当前对象。在递归过程中,引用计数为0的对象也都将被回收,比如说下图中的phone和address指向的对象。


引用计数_字符串_04




环形数据问题

但是这种引用计数算法有一个比较大的问题,那就是它不能处理环形数据 - 即如果有两个对象相互引用,那么这两个对象就不能被回收,因为它们的引用计数始终为1。这也就是我们常说的“内存泄漏”问题。比如下图展示的将p变量赋值为null值后所出现的内存泄漏。


引用计数_内存泄漏_05