关于某人说的,循环引用必须使用 gc 的探讨


#1

Gc 是一种失败的内存管理模式 继续讨论:

我不止一次的看到, 有人(包括 Herb Sutter)说, smart_ptr 无法解决循环引用的问题。因此必须要有 GC 来解决循环引用问题。

真的么?

这些人大概是 Java 代码写多了,认为循环引用的根源在于引用计数,不使用 gc 是解决不了的。

其实错了,出现循环引用正是说明你不了解 指针。

我们来看一个 循环引用的例子。在这个例子里

a -> b -> c 
|         |
+---------+

三者循环引用了。 如果他们相互之间使用了 shared_ptr , 则谁也释放不了。 在使用垃圾收集的语言里,如果没有外界的变量指向 a b c 的任何一个,则 a b c 会被垃圾回收。

问题就在这里他们需要一个 “外界变量” 来指向 a b c 。

他们宣称,使用 shared_ptr , 如果你使用外界的 智能指针指向 a b c , 就算外界的变量撤销, a b c 本身永远不会被释放。 所以 C++ 必须要有 GC 以解决这个问题。

真的需要 GC 么?

我们来研究一下 GC 是怎么回收 a b c 的。 首先, GC 的实现上, a b c 分配了内存, 这个分配的内存被放到一张全局的表里。 接着有外界的变量 v 指向 a .

  • GC 运行的时候, 发现 v 指向 a , a 指向 b , b 指向 c . 因此认为 a b c 还有引用的变量。不能收集。

  • 接着 v 退出了作用域。没有任何变量指向 a b c 了, 除了他们相互自己。

  • GC 再次运行,发现没有任何全局的或者调用栈上变量直接或者间接指向 a b c 。 于是 a b c 被回收。

在 shared_ptr 的情况下, a b c 绝对会因为相互引用导致无法回收。

其实,解决问题的办法就在 GC 的算法里, GC 有一个全局的对象表,记录了全部分配的对象。 GC 运行的时候就是扫描这个对象表。

那么在 C++ 里,可以借用这样的思路。

  • 首先我们有一个容器, vector , 存放了 a b c 三者的智能指针。
  • a b c 相互之间使用的是 weak_ptr
  • 可以从 weak_ptr 获取 share_ptr 以便使用 a b c
  • 当这段代码运行完毕,撤销容器即可。容器的撤销即导致 a b c 被释放。但是如果还有人持有 a b c 的 shared_ptr , 他们对象也不会被撤销,以防止悬空指针。当然,这些外部引用消失的时候, a b c 就真的释放了。

SEE?

其实 c++ 真心不需要一个语言内建的垃圾收集。很简单的道理, 因为其他语言里的 GC 本身是由 C++ 实现的。 因此 C++ 是可以实现 GC 的,而不需要语言内建。


#4

Herb Sutter什么时候在哪说过“没法解决循环引用”了?不就是扯并发问题? 跟不了解指针也没多少关系。C的发明者自己大概都对这里(所有权语义)没数,有稳定的idiom的话Java抄起来也不利索更不应该抢市场,大概也不会有后来Go的那种半吊子GC(就是GC里面也算实现的比较垃圾的)。 而且说到底你现在在结果上还不就是发明了个半吊子GC类似物。 与其如此还不如让ISO C++支持GC(不是说提供默认实现并鼓励滥用,而是指允许在语言实现的层次上——例如“引用”本身的内存表示——优化同时保持可移植性)。


#5

#6

真心不懂,看不明白。