为何在RAII可用时进行垃圾收集?

Alo*_*ave 41 c++ garbage-collection raii language-lawyer c++11

我听说C++ 14在C++标准库中引入垃圾收集器.这个功能背后的理由是什么?这不是RAII存在于C++中的原因吗?

  • 标准库垃圾收集器的存在将如何影响RAII语义?
  • 对我(程序员)或我编写C++程序的方式有什么影响?

Ali*_*Ali 30

垃圾收集和RAII在不同的环境中很有用.GC的存在不应影响您对RAII的使用.由于RAII是众所周知的,我举两个GC很方便的例子.


垃圾收集对于实现无锁数据结构非常有帮助.

[...]事实证明,确定性内存释放是无锁数据结构中的一个基本问题.(来自无锁数据结构作者:Andrei Alexandrescu)

基本上问题是你必须确保在线程读取时不释放内存.这就是GC变得方便的地方:它可以查看线程,只在安全时才进行解除分配.请阅读文章了解详情.

这里要明确一点:这并不意味着整个世界应该像Java一样被垃圾收集; 只有相关数据才能准确地进行垃圾收集.


在他的一个演讲中,Bjarne Stroustrup也提供了一个很好的,有效的例子,GC变得很方便.想象一下用C/C++编写的应用程序,大小为10M SLOC.该应用程序工作得相当好(相当无bug),但它泄漏.你既没有资源(工时)也没有功能知识来解决这个问题.源代码是一个有点混乱的遗留代码.你是做什么?我同意这可能是用GC解决问题的最简单,最便宜的方法.


正如sasha.sochka所指出的那样,垃圾收集器是可选的.

我个人担心的是人们会开始使用GC,就像在Java中使用它一样,会编写草率的代码并且垃圾收集所有内容.(我的印象shared_ptr已经成为默认'转到',即使在unique_ptr或者,地狱,堆栈分配会这样做.)

  • 当你必须使用堆时,垃圾收集也会派上用场,但是经常会释放对象.允许垃圾收集推迟释放资源,直到内存不足,然后立即收集所有内存.如果您的程序尚未经过优化以使用内存池或空闲列表但遇到此问题,则可能会受益于此事实. (3认同)
  • @Alex:还有其他方法来处理这样的事情,比如自定义分配器/解除分配器和智能指针 (2认同)

sas*_*hka 11

我同意@DeadMG当前C++标准中没有GC,但我想在B. Stroustrup中添加以下引文:

当(不是)自动垃圾收集成为C++的一部分时,它将是可选的

所以Bjarne确信它将来会被添加.至少EWG(进化工作组)的主席和最重要的委员会成员之一(更重要的是语言创建者)想要添加它.

除非他改变了他的观点,否则我们可以预期它将来会被添加和实施.


Cor*_*son 11

有些算法在没有GC的情况下编写复杂/低效/不可能.我怀疑这是GC在C++中的主要卖点,并且永远不会看到它被用作通用分配器.

为什么不是通用分配器?

首先,我们有RAII,而且大多数人(包括我)似乎都认为这是一种优越的资源管理方法.我们喜欢确定性,因为它使编写健壮,无泄漏的代码变得更加简单,并使性能可预测.

其次,您需要对如何使用内存进行一些非C++类似的限制.例如,您至少需要一个可到达的,未混淆的指针.在常见的树容器库中使用的混淆指针(使用对齐保证的低位用于颜色标记)等,GC将无法识别.

与此相关的是,如果您支持任意数量的混淆指针,那么使现代GC如此可用的东西将很难应用于C++ .分代碎片整理GC非常酷,因为分配非常便宜(基本上只是增加一个指针),最终你的分配会被压缩成更小的地方性.要做到这一点,物体需要是可移动的.

为了使对象安全地移动,GC需要能够更新它的所有指针.它将无法找到混淆的.这可以容纳,但不会很漂亮(可能是gc_pin类型或类似的,像电流一样std::lock_guard使用,无论何时需要原始指针都可以使用).可用性将会出现.

如果不使用移动设备,GC将比您在其他地方习惯的速度慢得多且可扩展性也差.

可用性原因(资源管理)和效率原因(快速,可移动的分配)不受影响,GC还有什么用呢?当然不是通用的.输入无锁算法.

为什么无锁?

无锁算法通过让争用操作与数据结构暂时"不同步"并在稍后的步骤中检测/纠正它来工作.这样做的一个结果是,在争用内存可能会在删除后被访问.例如,如果有多个线程竞争从LIFO弹出一个节点,则一个线程可能会弹出并删除该节点,然后另一个线程已意识到该节点已被占用:

线程A:

  • 获取指向根节点的指针.
  • 从根节点获取指向下一个节点的指针.
  • 暂停

线程B:

  • 获取指向根节点的指针.
  • 暂停

线程A:

  • 流行节点.(如果根节点指针在读取后没有更改,则将根节点指针替换为下一个节点指针.)
  • 删除节点.
  • 暂停

线程B:

  • 从我们的根节点指针获取指向下一个节点的指针,该指针现在"不同步"并且刚被删除,所以我们崩溃了.

使用GC,您可以避免从未提交的内存中读取数据,因为在线程B引用它时,永远不会删除该节点.有很多方法可以解决这个问题,例如危险指针或在Windows上捕获SEH异常,但这些可能会严重影响性能.GC往往是最优化的解决方案.


Pup*_*ppy 6

没有,因为没有一个.在C++ 11中引入了C++曾用于GC的唯一功能,它们只是标记内存,不需要收集器.在C++ 14中也不存在.

我认为,收藏家无法通过委员会.

  • 这不回答这个问题.你甚至不解释为什么你有这么强烈的意见. (22认同)
  • @anatolyg:这个问题询问C++ 14中垃圾收集的后果是什么.没有,因为没有.这是他的问题的答案.我的意见只是一个注释,真的. (7认同)
  • +1; C++ 14委员会草案也没有介绍垃圾收集:http://isocpp.org/files/papers/N3690.pdf (2认同)