C++ 标准库和 Boehm 垃圾回收器

6
我希望开发一个多线程的C++应用程序(其中大部分C++代码最终将由应用程序本身生成,这可以视为高级领域特定语言),在Linux/AMD64/Debian上使用GCC 4.6(并且可能是最新的C++11标准)。
我真的想为所有堆分配使用Boehm's conservative garbage collector,因为我想使用new(GC)进行分配,并且不必担心delete。我假设Boehm的GC足够好用。
使用C++(而不是C)的主要动机是C++标准库提供的所有算法和集合std::map ... std::vector
Boehm的GC提供了一个gc_allocator<T>模板(在其文件gc/gc_allocator.h中)。
我应该重新定义operator ::new作为Boehm的吗?
我应该使用带有显式分配器模板参数设置为 gc_allocator 的所有集合模板吗?我不完全理解std::vector的第二个模板参数(分配器)的作用?它用于分配向量内部数据还是分配每个单独的元素?
那么std::string呢?如何使它们的数据GC分配?我应该拥有自己的字符串,使用带有gc_allocatorbasic_string模板吗?是否有一些方法可以获得用GC_malloc_atomic而不是GC_malloc分配的字符内部数组?
还是您建议不要在由g++编译的应用程序中使用Boehm GC?
问候。

8
这完全取决于您对C++的熟悉程度和掌握程度。如果您能够编写一个不使用delete,而且明白只有在非常特殊的情况下才应该使用new,并且大部分情况下不需要使用指针的好的C++程序,那么如果您认为需要垃圾回收器,就可以使用它。另一方面,如果您不了解这些,您可能会发现以惯用的、现代化的C++方式实现用户友好的确定性内存管理是相当优秀的。 - Kerrek SB
5
嘿嘿 -- 我可能有些出格,但是在我看来,如果你 确实 知道垃圾回收,但 不是很 熟悉 C++,那么这就是一个经典的“手中有把锤子”的情况...为什么不发布一段典型的代码片段,我们可以看看如何用 C++ 的方法来做呢? - Kerrek SB
2
嗯,你真的必须选择一种。它们是不同的语言。当你生成C++代码并且做得正确时,我会说你不需要垃圾回收器,而且你需要证明为什么你需要一个。 (你的编译器可能会使用一个,但现在我正在谈论生成的代码。) - Kerrek SB
3
坦率地说,我认为问题太多了。最好专注并具体化。说实话,如果你对C++分配器机制不熟悉,设计C++代码生成工具可能还为时过早,接近这样的尝试前应该深入掌握你要翻译成的语言。这只是我的个人印象,我祝你好运和勇气 -- 我只是认为学习更多关于C++的知识对每个人都是双赢的。 - Kerrek SB
5
简单来说,没有任何理由直接使用new,也绝不需要使用delete。从你的文章中可以看出,我得到了一个清晰的印象,即你根本不知道如何编写C++代码,也不知道如何使用标准提供的结构,并且仅仅因为你习惯于GC,所以使用它作为范例。 - Puppy
显示剩余12条评论
1个回答

5
为了回答自己的问题,以下代码
// file myvec.cc
#include <gc/gc.h>
#include <gc/gc_cpp.h>
#include <gc/gc_allocator.h>
#include <vector>

class Myvec {
  std::vector<int,gc_allocator<int> > _vec;
public:
  Myvec(size_t sz=0) : _vec(sz) {};
  Myvec(const Myvec& v) : _vec(v._vec) {};
  const Myvec& operator=(const Myvec &rhs) 
    { if (this != &rhs) _vec = rhs._vec; return *this; };
  void resize (size_t sz=0) { _vec.resize(sz); };
  int& operator [] (size_t ix) { return _vec[ix];};
  const int& operator [] (size_t ix) const { return _vec[ix]; };
  ~Myvec () {};
};

extern "C" Myvec* myvec_make(size_t sz=0) { return new(GC) Myvec(sz); }
extern "C" void myvec_resize(Myvec*vec, size_t sz) { vec->resize(sz); }
extern "C" int myvec_get(Myvec*vec, size_t ix) { return (*vec)[ix]; }
extern "C" void myvec_put(Myvec*vec, size_t ix, int v) { (*vec)[ix] = v; }

使用g++ -O3 -Wall -c myvec.cc编译时,会生成一个带有以下特征的目标文件

 % nm -C myvec.o
                 U GC_free
                 U GC_malloc
                 U GC_malloc_atomic
                 U _Unwind_Resume
0000000000000000 W std::vector<int, gc_allocator<int> >::_M_fill_insert(__gnu_cxx::__normal_iterator<int*, std::vector<int, gc_allocator<int> > >, unsigned long, int const&)
                 U std::__throw_length_error(char const*)
                 U __gxx_personality_v0
                 U memmove
00000000000000b0 T myvec_get
0000000000000000 T myvec_make
00000000000000c0 T myvec_put
00000000000000d0 T myvec_resize

因此,在生成的代码中没有普通的malloc或::operator new

因此,通过使用gc_allocatornew(GC),我显然可以确保没有在我的不知情情况下使用普通的::opertor newmalloc,并且我不需要重新定义::operator new


附录(2017年1月)

供以后参考(感谢Sergey Zubkov在Quora的评论中提到),还可以参考n2670<memory>和垃圾收集支持(例如std::declare_reachable, std::declare_no_pointers, std::pointer_safety等)。然而,当前至少在GCC或者Clang中并未实现(除了将其作为无操作的平凡但可接受的方式)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接