这两天看PHP官方文档时,有对垃圾回收的相关描述。PHP的GC采用引用计数机制,在 5.3.0 版本之后使用文章 Concurrent Cycle Collection in Reference Counted Systems 中的同步算法。相较php之前使用的引用计数算法,该算法解决了对象的循环引用导致的内存不能回收问题。
文章对算法的描述比较清楚,其中有伪代码及其描述,大致分两部分:
1 对对象计数的增减,使用 Increment() 和 Decrement():
1 Increment(S) 2 RC(S) = RC(S) + 1 3 color(S) = black 4 5 Decrement(S) 6 RC(S) = RC(S) - 1 7 if (RC(S) == 0) 8 Release(S) 9 else 10 PossibleRoot(S) 11 12 Release(S) 13 for T in children(S) 14 Decrement(T) 15 color(S) = black 16 if (! buffered(S)) 17 Free(S) 18 19 PossibleRoot(S) 20 if (color(S) != purple) 21 color(S) = purple 22 if (! buffered(S)) 23 buffered(S) = true 24 append S to Roots
2 对无用对象内存的收集释放(包括循环应用对象) CollectCycles():
1 CollectCycles() 2 MarkRoots() 3 ScanRoots() 4 CollectRoots() 5 6 MarkRoots() 7 for S in Roots 8 if (color(S) == purple) 9 MarkGray(S) 10 else 11 buffered(S) = false 12 remove S from Roots 13 if (color(S) == black and RC(S) == 0) 14 Free(S) 15 16 ScanRoots() 17 for S in Roots 18 Scan(S) 19 20 CollectRoots() 21 for S in Roots 22 remove S from Roots 23 buffered(S) = false 24 CollectWhite(S) 25 26 MarkGray(S) 27 if (color(S) != gray) 28 color(S) = gray 29 for T in children(S) 30 RC(T) = RC(T) - 1 31 MarkGray(T) 32 33 Scan(S) 34 if (color(S) == gray) 35 if (RC(S) > 0) 36 ScanBlack(S) 37 else 38 color(S) = white 39 for T in children(S) 40 Scan(T) 41 42 ScanBlack(S) 43 color(S) = black 44 for T in children(S) 45 RC(T) = RC(T) + 1 46 if (color(T) != black) 47 ScanBlack(T) 48 49 CollectWhite(S) 50 if (color(S) == white and ! buffered(S)) 51 color(S) = black 52 for T in children(S) 53 CollectWhite(T) 54 Free(S)
其中有一点,在 MarkGray 的时候将所有引用计数减一,这时候针对同一对象的多个引用计数都会减一,所以如果只有有循环引用(没有其它引用)的对象的引用数会减到 <= 0(这里不太确定是不是会小于零),这个时候便可清除该对象。